What
Node.js事实上就是另外一种上下文,它允许在后端(脱离浏览器环境)运行JavaScript代码。
Process
- 我们需要提供Web页面,因此需要一个HTTP服务器
- 对于不同的请求,根据请求的URL,我们的服务器需要给予不同的响应,因此我们需要一个路由,用于把请求对应到请求处理程序(request handler)
- 当请求被服务器接收并通过路由传递之后,需要可以对其进行处理,因此我们需要最终的请求处理程序
- 路由还应该能处理POST数据,并且把数据封装成更友好的格式传递给请求处理入程序,因此需要请求数据处理功能
- 我们不仅仅要处理URL对应的请求,还要把内容显示出来,这意味着我们需要一些视图逻辑供请求处理程序使用,以便将内容发送给用户的浏览器
- 最后,用户需要上传图片,所以我们需要上传处理功能来处理这方面的细节
Demo
// 第一行请求(require)Node.js自带的 http 模块,并且把它赋值给 http 变量。
var http = require("http");
// 接下来我们调用http模块提供的函数: createServer 。
// 这个函数会返回一个对象,这个对象有一个叫做 listen 的方法
// 这个方法有一个数值参数,指定这个HTTP服务器监听的端口号。
http.createServer(function(request, response) {
// onRequest() 函数被触发的时候,有两个参数被传入: request 和 response 。
// 它们是对象,你可以使用它们的方法来处理HTTP请求的细节,并且响应请求(比如向发出请求的浏览器发回一些东西)
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
// 使用 response.write() 函数在HTTP相应主体中发送文本“Hello World"。
response.end();
}).listen(8888);
事件驱动
给某个方法传递了一个函数,这个方法在有相应事件发生时调用这个函数来进行 回调 。
在创建完服务器之后,即使没有HTTP请求进来、我们的回调函数也没有被调用的情况下,我们的代码还继续有效。
当收到请求时,使用 response.writeHead() 函数发送一个HTTP状态200和HTTP头的内容类型(content-type),使用 response.write() 函数在HTTP相应主体中发送文本“Hello World"。最后,我们调用 response.end() 完成响应。
行为驱动执行
将函数作为参数传递不仅仅出于技术上的考量也是个哲学问题
路由
为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码
查看HTTP请求,从中提取出请求的URL以及GET/POST参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的HTTP服务器的功能
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有不同的处理方式。例如处理/start的“业务逻辑”就应该和处理/upload的不同。
创建一个叫做requestHandlers的模块,并对于每一个请求处理程序,添加一个占位用函数,随后将这些函数作为模块的方法导出:
function start() {
console.log("Request handler 'start' was called.");
}
function upload() {
console.log("Request handler 'upload' was called.");
}
exports.start = start;
exports.upload = upload;
结合到一起
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
server.start(router.route, handle);
阻塞和非阻塞
Node.js可以在不新增额外线程的情况下,依然可以对任务进行并行处理 —— Node.js是单线程的。它通过事件轮询(event loop)来实现并行操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。
child_process
hild_process模块,主要是用的child_process.spawn(),需要注意的是,这个函数只会创建异步进程。异步进程的话,不会阻塞主进程的执行。它其实是个很好的东西,有了它,我们可以执行非常耗时的shell操作而无需迫使我们的应用停下来等待该操作。
实际使用
让我们来看看如何处理POST请求(非文件上传),之后,我们使用Node.js的一个用于文件上传的外部模块。之所以采用这种实现方式有两个理由。
第一,尽管在Node.js中处理基础的POST请求相对比较简单
第二,用Node.js来处理文件上传(multipart POST请求)是比较复杂的
处理POST 请求
POST请求一般都比较“重” —— 用户可能会输入大量的内容。用阻塞的方式处理大数据量的请求必然会导致用户操作的阻塞。
为了使整个过程非阻塞,Node.js会将POST数据拆分成很多小的数据块,然后通过触发特定的事件,将这些小数据块传递给回调函数。这里的特定的事件有data事件(表示新的小数据块到达了)以及end事件(表示所有的数据都已经接收完毕)。
将data和end事件的回调函数直接放在服务器中,在data事件回调中收集所有的POST数据,当接收到所有数据,触发end事件后,其回调函数调用请求路由,并将数据传递给它,然后,请求路由再将该数据传递给请求处理程序。
index.js 是主文件
sever.js 是HTTP服务器模块,当回调启动时候,onRequest()函数被触发,两个参数传入request和response–> 使用它们的方法来处理HTTP请求的细节,并且响应请求。