Nodejs笔记(二)

时间:2021-12-08 11:58:57

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=Mary@163.com",
query:{name:"Mary",email:"Mary@163.com"},
pathname:"/user",
path:"/user?name=Mary&email=Mary@163.com",
search:"/user?name=Mary&email=Mary@163.com"
}

获取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);