Following the positive response from a similar article based on Node, I found it fitting that I demonstrate how you can handle the payload and parameters sent as part of a POST request made to a Dart server backend. 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();
}
}
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
And you should be greeted with a form in the browser:
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();
}
}
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();
} ...
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';
And use the Utf8Decoder
as our transformer:
var content = await req.transform(Utf8Decoder()).join();
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
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;
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();
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);
Our query params are returned as a Map
object, allowing us to pull our values like this:
queryParams['fname'];
queryParams['age'];
queryParams['photo'];
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();
}
}
}
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.