在整本书中编写一个小型的相册网站。本章将会编写一个JSON服务器,用以提供相册列表以及每个相册对应的照片列表等服务,最后,还会添加为相册重命名的功能。
4.1 第一个JSON服务器
var http = require('http');
var fs = require('fs');
function load_album_list(callback) {
fs.readdir(
'albums',
function (err, files) {
if (err) {
callback(err);
return;
}
callback(null, files);
}
);
}
function handle_incoming_request(req, res) {
console.log("INCOMING REQUEST: " + req.method + " " + req.url);
load_album_list(function (err, albums) {
if (err) {
res.writeHead(503, {"Content-Type" : "application/json"});
res.end(JSON.stringify(err) + "\n");
return;
}
var out = {
error: null,
data: { albums : albums }
};
res.writeHead(200, {"Content-Type" : "application/json" });
res.end(JSON.stringify(out) + "\n");
});
}
var s = http.createServer(handle_incoming_request);
s.listen(8080);
读取albums/的文件服务器
4.2 Node模式:异步循环
var http = require('http');
var fs = require('fs');
function load_album_list(callback) {
fs.readdir(
'albums',
function (err, files) {
if (err) {
callback(err);
return;
}
var only_dirs = [];
//for (var i=0; i<files.length; i++) {
(function iterator(index) { //使用递归
if (index == files.length) {
callback(null, only_dirs);
return ;
}
//
fs.stat(
"albums/" + files[index],
function(err, stats) {
if (err) {
callback(err);
return;
}
if (stats.isDirectory()) {
only_dirs.push(files[index]);
}
iterator(index + 1);
}
);
})(0);
}
);
}
function handle_incoming_request(req, res) {
console.log("INCOMING REQUEST: " + req.method + " " + req.url);
load_album_list(function (err, albums) {
if (err) {
res.writeHead(503, {"Content-Type" : "application/json"});
res.end(JSON.stringify(err) + "\n");
return;
}
var out = {
error: null,
data: { albums : albums }
};
res.writeHead(200, {"Content-Type" : "application/json" });
res.end(JSON.stringify(out) + "\n");
});
}
var s = http.createServer(handle_incoming_request);
s.listen(8080);
4.3 小戏法:处理更多的请求
var http = require('http');
var fs = require('fs');
function load_album_list(callback) {
fs.readdir(
'albums',
function (err, files) {
if (err) {
callback(make_error("file_error", JSON.stringify(err)));
return;
}
var only_dirs = [];
//for (var i=0; i<files.length; i++) {
(function iterator(index) { //使用递归
if (index == files.length) {
callback(null, only_dirs);
return ;
}
//
fs.stat(
"albums/" + files[index],
function(err, stats) {
if (err) {
callback(make_error("file_error", JSON.stringify(err)));
return;
}
if (stats.isDirectory()) {
var obj = { name: files[index] };
only_dirs.push(obj);
}
iterator(index + 1);
}
);
})(0);
}
);
}
function load_album(album_name, callback) {
fs.readdir(
'albums/' + album_name,
function (err, files) {
if (err) {
if (err.code == "ENOENT") {
callback(no_such_album());
} else {
callback(make_error("file_error", JSON.stringify(err)));
}
return;
}
var only_files = [];
var path = "albums/" + album_name + "/";
//for (var i=0; i<files.length; i++) {
(function iterator(index) { //使用递归
if (index == files.length) {
var obj = {
short_name : album_name,
photos: only_files
};
callback(null, obj);
return ;
}
//
fs.stat(
path + files[index],
function(err, stats) {
if (err) {
callback(make_error("file_error", JSON.stringify(err)));
return;
}
if (stats.isFile()) {
var obj = {
filename: files[index],
desc : files[index]
};
only_files.push(obj);
}
iterator(index + 1);
}
);
})(0);
}
);
}
function handle_incoming_request(req, res) {
console.log("INCOMING REQUEST: " + req.method + " " + req.url);
if (req.url == '/albums.json') {
handle_list_albums(req, res);
} else if (req.url.substr(0, 7) == '/albums'
&& req.url.substr(req.url.length - 5) == '.json') {
handle_get_albums(req, res);
} else {
send_failure(res, 404, invalid_resource());
}
}
function handle_list_albums(req, res) {
load_album_list(function (err, albums) {
if (err) {
send_failure(res, 500, err);
return;
}
send_success(res, { albums: albums });
});
}
function handle_get_albums(req, res) {
var album_name = req.url.substr(7, req.url.length - 12);
load_album(
album_name,
function (err, albums_contents) {
if (err && err.error == "no_such_album") {
send_failure(res, 404, err);
} else if (err) {
send_failure(res, 500, err);
} else {
send_success(res, { album_data : albums_contents });
}
});
}
function make_error(err, msg) {
var e = new Error(msg);
e.code = err;
return e;
}
function send_success(res, data) {
res.writeHead(200, {"Content-Type" : "application/json"});
var output = { error: null, data: data };
res.end(JSON.stringify(output) + "\n");
}
function send_failure(res, server_code, err) {
var code = (err.code) ? err.code : err.name;
res.writeHead(server_code, { "Content-Type" : "application/json" });
res.end(JSON.stringify({ error: code, message: err.message }) + "\n");
}
function invalid_resource() {
return make_error("invalid_resource", "the requested resource does not exist.");
}
function no_such_album() {
return make_error("no_such_album", "The specified album does not exist");
}
var s = http.createServer(handle_incoming_request);
s.listen(8080);
4.4 请求和响应对象的更多细节
HTTP状态码:
4.5 提高灵活性:GET参数
4.6 修改内容:POST数据
curl -s -X POST -H "Content-Type: application/json" -d '{"album_name":"new album name"}' http://localhost:8080/albums/old_album_name/rename.json
接收JSON POST数据: 当使用Node的异步非阻塞特性时,数据流是传输大量数据的最佳方式。
on('readable', ...)的处理程序函数都会被调用,首先通过read方法读取来自数据流的数据并将这些传入的数据添加到json_body变量的后面;然后当监听到end事件,会得到这些结果字符串,并尝试去解析它。当给定的字符串不是一个合法的JSON时,JSON.parse会抛出一个错误,所以必须用try/catch语句块封装这些代码。
接收表单POST数据:
<form name="simple" method='post' action="http://localhost:8080">
Name: <input name='name' type='text' size='10' /><br/>
Age: <input name='age' type='text' size='5' /><br/>
<input type='submit' value='Send'/>
</form>
var http = require('http'), qs = require('querystring');
function handle_incoming_request(req, res) {
var body = '';
req.on('readable', () => {
var d = req.read();
if (d) {
if (typeof d == 'string') {
body += d;
} else if (typeof d == 'object' && d instanceof Buffer) {
body += d.toString('utf8');
}
}
});
// 3. when we have all the post data, make sure we have valid
// data and then try to do the rename.
req.on('end', () => {
if (req.method.toLowerCase() == 'post') {
var POST_data = qs.parse(body);
console.log(POST_data);
}
res.writeHead(200, { "Content-Type" : "application/json" });
res.end(JSON.stringify( { error: null }) + "\n");
});
}
var s = http.createServer(handle_incoming_request);
s.listen(8080);