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