Jermaine
Posted on October 10, 2018
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();
}
}
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.
Further reading
Posted on October 10, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.