Work
with us

Tell us about your idea
and we will find a way
to make it happen.

Get estimate

Join our awesome team

Check offers

In my last two entries I covered get requests and parsing json. In this part I want to show how to prepare POST request and create an entry inside our rails app.

Before we start I encourage You to download the sample code I created from binar::apps’ github,C++ application and the Rails one. One note, if you have downloaded my rails app previously, please update your version.

Let’s get started!

This C++ application is a bit tricky since I used asynchronous methods by using the so calledworker Lets take a quick look how the request looks like by using this script sudo tcpdump -s 0 -A -i lo0 'tcp port 3000 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' Where lo0 is a loopback interface and our app is running on the 3000 port. Of course you might need to change those settings if you want to inspect an online app.

POST /entries HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pl,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:3000/entries/new
Cookie: cookies_accepted=T; _ruszamysie_session=RVc3U1kyeWtoci9oMVhZT1lOQXp0WEh0R1BGTXdmcDM1RkNVNnpSQ1dsWER6NWRWNmdoZlgvclBwdGdPRTdBY29WdjY0ekhjZzA1aCtTcW5JeXlkd2Zpa0YvV25Ib3VzcTRSbWFXdzZTQ1FlbFZnaS8rdkpOS08yYXZuVy8zcituVk9KUUE1WTdwZkxNUlJDQnF3c2cyMDRpV29SaWRuMmg3MERMNi9EbGNqeUNKc3ZZbTdPdHRYdkRDbkpFZ1lHNFhscElGYjlrelAxZnpXWkQxR3JJeWw5QmIzbTd6aW9zbTdQUlhDUUppaVIzeFJoSGRQN2JDQU1vVGtQNm80MGI2Skw5MFU2ZVZ3aDZ1ZVZraFZXMTBrZGF2MERNcmhBMnUvT1p5OUJPK08yQklMWW5RWkRIUk55Z3lydk9wMGYrQWZzRmRBVE1ObFRSQk9ySCtZdnA3YXRxZjVtZWtnRkJjcmZsZGQrYWRWb0ZjdHdsZTdhV0VoT1dnWGJUbndNLS1BZ3lCN2FuWTA1eSs2VGU1WndsdFd3PT0%3D--771c7bff49ca8b8cb73b54b1b1e131010a7d78bb; remember_user_token=W1sxXSwiJDJhJDEwJG1VbDB4UGtMSjFmV0cvc2toMXZkQ2UiXQ%3D%3D--1438bb505f1e521ee078eddcb8ad038ffa3171a1; _api_session=WEZxVHlod0ptNGxkaG9jYmdDMUc0dEo1YXdOeTd4MDMwQ3N6UTI2RG9SL1RwQWx5dElxbWlhV1RZd0hLK3BZaFZJN0VhdCtLZ2dIdUhqc29pNUIxdlZURDVkQTdCTVYzQ3RXRjJkcWZqQmVtVXVLK2M1YTdYWXplQXJ4cjRzK2w1dVBLMW5QM2h5S0FkSCtIUG92cno5WUt2b2Jia2o0aDR5K0xvWEkwNjFhR0lkRDU4UzhzZjBRYVJzTENQYkxLLS1rak45WjUwNEZGTGtqeDRTVlRMZzlRPT0%3D--178fc9c960ce753c16eb69c9f74270257f614e4d
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------475104419735770587885416283
Content-Length: 405174

-----------------------------475104419735770587885416283
Content-Disposition: form-data; name="utf8"

...
-----------------------------475104419735770587885416283
Content-Disposition: form-data; name="authenticity_token"

tjZJCxmxL+Flb+pc0YxYZ0eLCL43Vrc6ObI30EscrBk=
-----------------------------475104419735770587885416283
Content-Disposition: form-data; name="entry[name]"

sample entry
-----------------------------475104419735770587885416283
Content-Disposition: form-data; name="entry[text]"

sample text
-----------------------------475104419735770587885416283
Content-Disposition: form-data; name="entry[file]"; filename="Zrzut ekranu 2014-08-27 o 09.38.54.png"
Content-Type: image/png

.PNG
(Long Binary data)
-----------------------------475104419735770587885416283
Content-Disposition: form-data; name="commit"

Submit
-----------------------------475104419735770587885416283--

This kind of a POST request is called multipart request. To create such a request inside my C++ application I used QHttpMultiPart Multipart requests allows you to upload files to server. It consists of several parts. Take a look at the example I posted above, it is clearly visible that we send 6 parts which are separated by the boundary.

Creating Simple QHttpPart

Each part consists of a header and body. The header shows the name of the parameter, type etc. While the body holds the content. To create the simplest QHttpPart (textual, not file part), you can use The following function:

QHttpPart DataSender::createPart(const QString &header, const QString &body)
{
    QHttpPart part;
    part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(header));
    part.setBody(body.toUtf8());
    return part;
}

To call it in the proper way please look at the request caught by tcpdump. Get the proper header – for example form-data; name=\"entry[name]\" for sending the entry’s field name.

Creating file part

Sending files is a bit more complicated than pure text, but still fairly easy thanks to QT. Let’s take a look at the header Content-Disposition: form-data; name="entry[file]"; filename="Zrzut ekranu 2014-08-27 o 09.38.54.png" Content-Type: image/pngAs you can see apart from the form name, we also need to include filename, file type and of course the file as such.

QHttpPart DataSender::createFilePart(const QString &header, QFile * file)
{
    QHttpPart part;
    QFileInfo info(*file);
    QString extension = info.suffix();
    QString filename = info.fileName();
    part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QString("image/%1").arg(extension)));
    part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(
    QString("%1; filename=\"%2\"").arg(header).arg(filename)));
    part.setBody(file->readAll());
    return part;
}

Please note that in this example I used a very simple method of obtaining information on file type – I just take the file suffix.

Putting it all together

As I mentioned before multipart request consists of several parts. Now is the time to create QHttpMultipart by joining all other components.

QHttpMultiPart * DataSender::prepareMultipart()
{
  QHttpMultiPart * multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
  multiPart->append(createPart("form-data; name=\"entry[name]\"", this->name));
  multiPart->append(createPart("form-data; name=\"entry[text]\"", this->text));
  QFile file(this->filepath);
  file.open(QIODevice::ReadOnly);
  if(file.isOpen())
  multiPart->append(createFilePart("form-data; name=\"entry[file]\"", &file));
  return multiPart;
}

As simple as that. Just create a bunch of parts and join them together in one big request. While using rails application to handle this request, the order of parts does not matter! I am not sure how it will work using other programing languages and frameworks.

CSRF token

Rails by default prohibits postdelete and patch methods from being executed without previous session. This is the way of defending against CSRF attacks, please take a closer look about themhere Unfortunately we need to execute a post action without the session. To turn off CSRF authenticity token for one method I use skip_before_action :verify_authenticity_token inside entries controller. Of course you might want to turn off authentication only for given actions to do so just add only: [action] after the comma.

Conclusion

I hope this post will help to to create your own application which use post request. Thanks for reading!

Post tags: