Node实战:4 编写简单应用

    在整本书中编写一个小型的相册网站。本章将会编写一个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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值