DEV Community

loading...
Cover image for How to handle the POST request body without using a framework

How to handle the POST request body without using a framework

graphicbeacon profile image Jermaine Oppong Originally published at creativebracket.com Updated on ・4 min read

Following the positive response from a similar article based on Node, I found it fitting that I demonstrate how this is possible in Dart. Long story short, I found it's simpler.

This example works where the Content-Type of the request payload is application/x-www-form-urlencoded, which essentially means that the form data is formatted as a query string when sent to the server.

1. Create our server

Create a main.dart file and enter the snippet below:

import 'dart:io';

void main() async {
  var server = await HttpServer.bind('localhost', 9000);

  await for (HttpRequest req in server) {
    req.response
      ..headers.set('Content-Type', 'text/html')
      ..write('''
        <!doctype html>
        <html>
        <body>
          <form action="/" method="post">
            <input type="text" name="fname" /><br />
            <input type="number" name="age" /><br />
            <input type="file" name="photo" /><br />
            <button>Save</button>
          </form>
        </body>
        </html>
      ''')
      ..close();
  }
}
Enter fullscreen mode Exit fullscreen mode

This bootstraps our server and responds with a form when a request is made to http://localhost:9000. The snippet begins by importing the dart:io library, since it contains the classes we'll need to create our server. The whole bootstrapping process happens in a main() function, which is needed to run our Dart app.

To run this file, type the below command in the terminal:

dart main.dart
Enter fullscreen mode Exit fullscreen mode

And you should be greeted with a form in the browser:

localhost

2. Capture the POSTed payload

Let's now ensure that we are dealing with a POST request:

...
...
await for (HttpRequest req in server) {
  if (req.method == 'POST') {
   // deal with the payload  
  } else {
    req.response
      ..headers.set('Content-Type', 'text/html')
      ..write('''
        <!doctype html>
        <html>
        <body>
          <form action="/" method="post">
            <input type="text" name="fname" /><br />
            <input type="number" name="age" /><br />
            <input type="file" name="photo" /><br />
            <button>Save</button>
          </form>
        </body>
        </html>
      ''')
      ..close();
  }
}
Enter fullscreen mode Exit fullscreen mode

Requests to the server are treated as a Stream, which means that we can use the request's transform method to decode the content.

if (req.method == 'POST') {
  var content = await req.transform().join();
} ...
Enter fullscreen mode Exit fullscreen mode

This won't work straightaway because the transform method requires a StreamTransformer. A StreamTransformer contains a bind method that allows it to manipulate the streaming data somehow. We need one to transform our streaming request data into a readable format, and afterwards use the join method to combine our transformed chunks.

Beneath our dart:io import, let's require the dart:convert library:

import 'dart:io';
import 'dart:convert';
Enter fullscreen mode Exit fullscreen mode

And use the Utf8Decoder as our transformer:

var content = await req.transform(Utf8Decoder()).join();
Enter fullscreen mode Exit fullscreen mode

Printing out content will give you the below result, provided you filled in the form with the relevant details:

fname=John&age=30&photo=file.jpg
Enter fullscreen mode Exit fullscreen mode

However, we still need to extract our key/value pairs of information from the query string. Fortunately, we have a Uri class in the core Dart SDK:

var content = await req.transform(Utf8Decoder()).join();
var queryParams = Uri(query: content).queryParameters;
Enter fullscreen mode Exit fullscreen mode

3. Respond to the POST

Now let's send back a response:

var content = await req.transform(Utf8Decoder()).join();
var queryParams = Uri(query: content).queryParameters;

req.response
  ..write('Parsed data belonging to ${queryParams['fname']}')
  ..close();
Enter fullscreen mode Exit fullscreen mode

UPDATE 20/10/2018: Tobe Osakwe(Creator of Angel) helpfully pointed out the use of splitQueryString static method to extract the query parameters:

// var queryParams = Uri(query: content).queryParameters;
var queryParams = Uri.splitQueryString(content);
Enter fullscreen mode Exit fullscreen mode

Our query params are returned as a Map object, allowing us to pull our values like this:

queryParams['fname'];
queryParams['age'];
queryParams['photo'];
Enter fullscreen mode Exit fullscreen mode

Solution

Here's the full solution:

import 'dart:io';
import 'dart:convert';

void main() async {
  var server = await HttpServer.bind('127.0.0.1', 9000);

  await for (HttpRequest req in server) {
    if (req.method == 'POST' && req.headers.contentType.toString() == 'application/x-www-form-urlencoded') {
      var content = await req.transform(Utf8Decoder()).join();
      var queryParams = Uri(query: content).queryParameters;
      req.response
        ..write('Parsed data belonging to ${queryParams['fname']}')
        ..close();
    } else {
      req.response
        ..headers.set('Content-Type', 'text/html')
        ..write('''
          <!doctype html>
          <html>
          <body>
            <form action="/" method="post">
              <input type="text" name="fname" /><br />
              <input type="number" name="age" /><br />
              <input type="file" name="photo" /><br />
              <button>Save</button>
            </form>
          </body>
          </html>
        ''')
        ..close();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In closing...

Like the related article, this works for application/x-www-form-urlencoded content types and would require a different approach if parsing other content types. We'll look at this in a future article.

Like, share and follow me 😍 for more content on Dart.


Further reading

  1. Hello world server in Dart
  2. Creating Streams in Dart
  3. Free Dart Screencasts on Egghead.io

Discussion (1)

pic
Editor guide
Collapse
formkeep profile image
FormKeep • Edited

Great article Jermain.

Another way to handle a POST without a framework is using a form backend like formkeep.com.

Using FormKeep, the action attribute simply needs to be updated to something like:

<form accept-charset="UTF-8" action="https://formkeep.com/f/exampletoken" method="POST">

FormKeep then handles the backend data storage, management and connectivity to other applications and services.