Yesterday, I was able to code a Dart-based web server with surprising ease. Currently, it only replies with "Hello" to requests to the root URL and 404s for everything else, but it's a start. Today, I would like to serve up files from the filesystem and serve up JSON from alternate resource locations.
I think that I must have played with JSON in Dart at some point. If I am going to replace the node.js backend in my Dart Comics sample app, I need to refresh my memory how to do JSON in Dart. Without looking at the documentation, I try the following:
app.addRequestHandler(
(req) => req.method == 'GET' && req.path == '/json',
(req, res) {
var data = {
'title': 'Watchmen',
'author': 'Alan Moore'
};
res.outputStream.writeString(data.toJSON);
res.outputStream.close();
}
);
I build a simple HashMap
and try to call toJSON
on it. Of course, I soon find out that there is no toJSON
method/getter. And shortly after that, I rediscover the dart:json
library:#import('dart:io'); #import('dart:json'); main() { HttpServer app = new HttpServer(); // ... app.addRequestHandler( (req) => req.method == 'GET' && req.path == '/json', (req, res) { var data = { 'title': 'Watchmen', 'author': 'Alan Moore' }; res.outputStream.writeString(JSON.stringify(data)); res.outputStream.close(); } ); app.listen('127.0.0.1', 8000); }That does the trick as I am now able to receive a JSON response:
➜ scripts git:(M1) ✗ curl -i http://localhost:8000/json HTTP/1.1 200 OK transfer-encoding: chunked {"title":"Watchmen","author":"Alan Moore"}%Easy enough. I am a bit surprised that I forgot the
dart:json
library entirely like that. I will chalk it up to advancing age. Happily once remembered, I did not have to look up the stringify
method—of course that is what it is named.Before moving on, I would like to set a couple of header values. These I do look up, though perhaps I could have guessed them as well:
app.addRequestHandler(
(req) => req.method == 'GET' && req.path == '/json',
(req, res) {
var data = {
'title': 'Watchmen',
'author': 'Alan Moore'
};
res.contentLength = JSON.stringify(data).length;
res.headers.contentType = 'application/json';
res.outputStream.writeString(JSON.stringify(data));
res.outputStream.close();
}
);
With that, I have my JSON resource responding as desired:➜ scripts git:(M1) ✗ curl -i http://localhost:8000/json HTTP/1.1 200 OK content-length: 42 content-type: application/json {"title":"Watchmen","author":"Alan Moore"}%Now let's see how hard it is to read a file from the file system to be served up to the client. The express.js / node.js backend that I currently use serves up the file in
public/index.html
. In express.js, this just requires a configuration setting to describe the location of the “public” directory. Were I coding it by hand in node.js, I would pipe a file reader stream to the response's output stream. Streams as a first-order concept in node is one of the many things that makes node so appealing. I wonder if I can to the same with Dart.
Indeed, there is a pipe() method on an input stream. That seems promising. So I change the responder for the root URL to be:
app.addRequestHandler(
(req) => req.method == 'GET' && req.path == '/',
(req, res) {
var file = new File.fromPath('public/index.html');
var stream = file.openInputStream();
stream.pipe(res.outputStream);
res.outputStream.close();
}
);
I am not quite sure if that last close()
is needed anymore. I suspect not, but there is one way to find out for certain. Only when I start the server and access the URL, I get a server crash.D'oh! It seems the
fromPath
named constructor needs a Path
object. What I really wanted there was the regular constructor: // ...
var file = new File('public/index.html');
// ...
That gets me further, but a request to the root URL now crashes the server on that close()
line that worried me. So I remove it, leaving the root URL responder as: app.addRequestHandler(
(req) => req.method == 'GET' && req.path == '/',
(req, res) {
var file = new File('public/index.html');
var stream = file.openInputStream();
stream.pipe(res.outputStream);
}
);
With that, a request to the root URL results in the homepage:➜ scripts git:(M1) ✗ curl -i http://localhost:8000/ HTTP/1.1 200 OK transfer-encoding: chunked <!DOCTYPE html> <html> <head> <title>Dart Comics</title> <link rel="stylesheet" href="/stylesheets/style.css"> <script src="/scripts/dart.js"></script> <script src="/scripts/web/main.dart" type="application/dart"></script> </head> <body> <h1>Dart Comics</h1> <p>Welcome to Dart Comics</p> <ul id="comics-list"></ul> <p id="add-comic">Add a sweet comic to the collection.</p> </body> </html>That… is nice. I am excited to see stream support like that in the Dart IO library. I would not expect that that it is as extensive as node.js just yet, but it is promising to see it work so well in a simple case like this.
I call it night here. Up tomorrow, I will attempt to hack in my own public directory handler.
Day #566
No comments:
Post a Comment