Nodejs事件
Node.js 所有的异步I/O 操作在完成时都会发送一个事件到事件队列。
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。可以通过require("events")来访问该模块。
EventEmitter 的用法:
//event.js var EventEmitter = require(‘events‘).EventEmitter; var event = new EventEmitter(); event.on(‘some_event‘, function() { console.log(‘some_event occured.‘); }); setTimeout(function() { event.emit(‘some_event‘); }, 1000);
运行代码,1秒后控制台输出了 ‘some_event occured‘。原理是 event 对象 注册了事件 some_event 的一个监听器,然后通过 setTimeout 在1000毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。
EventsEmitter
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
EventEmitter 的每个事件由一个事件名和N个参数组成,对于每个事件,EventEmitter 支持若干个事件监听器。当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。回调函数是按照顺序依次被调用的。
var events = require(‘events‘); var emitter = new events.EventEmitter(); emitter.on(‘someEvent‘, function(arg1, arg2) { console.log(‘listener1‘, arg1, arg2); }); emitter.on(‘someEvent‘, function(arg1, arg2) { console.log(‘listener2‘, arg1, arg2); }); emitter.emit(‘someEvent‘, ‘void‘, 2008);
结果是
listener1 void 2008 listener2 void 2008
EventEmitter.on(event, listener)、emitter.addListener(event, listener) 为指定事件注册一个监听器,接受一个字符串event 和一个回调函数listener。
server.on(‘connection‘, function (stream) { console.log(‘someone connected!‘); });
EventEmitter.emit(event, [arg1], [arg2], [...]) 触发event 事件,传递若干可选参数到事件监听器的参数表。
EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。
server.once(‘connection‘, function (stream) { console.log(‘We have our first user!‘); });
EventEmitter.removeListener(event, listener) 移除指定事件的某个监听器,listener 必须是该事件已经注册过的监听器。
var callback = function(stream) { console.log(‘someone connected!‘); }; server.on(‘connection‘, callback); // ... server.removeListener(‘connection‘, callback);
Nodejs路由
有时要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码。
因此,需要查看HTTP请求,从中提取出请求的URL以及GET/POST参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的HTTP服务器的功能。
所需要的所有数据都会包含在request对象中,该对象作为onRequest()回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的Node.JS模块,它们分别是url和querystring模块。也可以用querystring来解析POST的数据。
var http = require("http"); var url = require("url"); function start() { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;
这个应用现在可以通过请求的URL路径来区别不同请求了--这使我们得以使用路由(还未完成)来将请求以URL路径为基准映射到处理程序上。
在所要构建的应用中,这意味着来自/start和/upload的请求可以使用不同的代码来处理。
建立一个名为router.js的文件,添加以下内容:
function route(pathname) { console.log("About to route a request for " + pathname); } exports.route = route;
服务器应当知道路由的存在并加以有效利用。当然可以通过硬编码的方式将这一依赖项绑定到服务器上,但是这会是一件痛苦的事,可以使用依赖注入的方式较松散地添加路由模块。
首先,可以扩展一下服务器的start()函数,以便将路由函数作为参数传递过去:
var http = require("http"); var url = require("url"); function start(route) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(pathname); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;
同时,需要扩展index.js,使得路由函数可以被注入到服务器中:
var server = require("./server"); var router = require("./router"); server.start(router.route);
如果现在启动应用(node index.js),随后请求一个URL,将会看到应用输出相应的信息,这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由:
bash$ node index.js Request for /foo received. About to route a request for /foo
Nodejs GET/POST请求
在很多场景中,服务器都需要跟浏览器打交道,如表单提交,Ajax请求等。表单提交到服务器一般都使用GET/POST请求。
获取GET请求的内容:
由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此可以手动解析后面的内容作为GET请求的参数。node.js中url模块中的parse函数提供了这个功能。
var http = require(‘http‘); var url = require(‘url‘); var util = require(‘util‘); http.createServer(function(req, res){ res.writeHead(200, {‘Content-Type‘: ‘text/plain‘}); res.end(util.inspect(url.parse(req.url, true))); }).listen(3000);
在浏览器中访问http://localhost:3000/user?name=Mary&email=Mary@163.com
然后查看返回结果:
{ protocol:null, slashes:null, auth:null, host:null, port:null, hostname:null, hash:null, search:"?name=Mary&[email protected]", query:{name:"Mary",email:"[email protected]"}, pathname:"/user", path:"/user?name=Mary&[email protected]", search:"/user?name=Mary&[email protected]" }
获取POST内容
POST请求的内容全部的都在请求体中,http.ServerRequest并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。
比如上传文件,而很多时候可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所有node.js默认是不会解析请求体的,当需要的时候,需要手动来做。
var http = require(‘http‘); var querystring = require(‘querystring‘); var util = require(‘util‘); http.createServer(function(req, res){ var post = ‘‘; //定义了一个post变量,用于暂存请求体的信息 req.on(‘data‘, function(chunk){ //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中 post += chunk; }); req.on(‘end‘, function(){ //在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。 post = querystring.parse(post); res.end(util.inspect(post)); }); }).listen(3000);