NodeJS Guide

来源:互联网 发布:frp 自定义域名 编辑:程序博客网 时间:2024/06/06 02:52

这个文章翻译自-https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/ 完全当做练手和学习nodejs.

这篇文章的意义是给出一个关于nodejs http处理的基础概念.我们假定你了解了http请求如何工作的和熟悉一点点nodejs EventEmitters和Steams. 如果你不是很熟悉他们,那么你应该花一点时间快速浏览下相关的api.

构建一个服务器

任何nodejs版本的web服务器应用必须在某个时间点创建出一个web server对象. 这个可以通过如下方法createServer完成:

/** * Created by edward.gao on 26/04/2017. */var http = require("http");var server = http.createServer(function (request, response) {   // 在这里做你想做的 });

传进createServer的函数会在server收到请求后执行一次. 事实上,通过createServer返回的Server对象就是一个EventEmitter对象. 这里我们要做的就是创建一个server对象然后添加监听器listener.

var server = http.createServer();server.on("request", function (request, response) {    // 同样的 在这里做你想做的});


当一个HTTP request到了服务器, node(nodejs驱动程序,这么理解比较简单,类似java中的java命令,译者注)调用请求处理函数来处理相关的事务. 这其中包括requestresponse对象. 这个我们后面会说道.


为了真正能够接收请求, 需要在server对象上调用listen方法. 在大多数情况下, 你需要做的就是传一个你想监听的端口进去.

Method URL 和headers

当处理一个请求时,第一件你可能需要想看看的是请求的方法和URL, 这样我们才能采取合适的处理动作. node将这些属性放在request对象上这样你可以很方便的拿到.

var method = request.method;var url = request.url;// 注意request对象是一个IncomingMessage的实例    

这里的method通常需要时一个合法的HTTP 方法或者动作.(GET/POST/CONNECT或者其他,译注) 这里的url是一个完整的不带服务器,端口,协议的URL地址. 对于一个典型的url地址来说就是包括第三个斜杠及以后的意思.(也就是/app/path?arg=1).

headers也差不多. 他们在自己的属性上:

    var headers = request.headers;    var userAgent = headers["user-agent"];

这里需要注意到所有在headers里面的key都是小写的 不管客户端是怎么发的.这让解析headers变得更加简单.
如果有的headers重复了,他们的值会被覆盖或者用逗号分隔(根据header不同而不同). 在某些情况下这个可能会有问题, 可以用rawHeaders来解决.

请求体

当接收一个POST或者PUT请求的时候,请求体的内容就很重要了. 处理请求体的时候就比较麻烦一点.request对象实现了ReadableStream接口,这个流可以被监听或者重定向到任何地方就向其他流一样.我们可以通过监听流的dataend事件来从流中获取数据.

data事件中带出(射出,emmitted,whatever)的chunk数据是一个Buffer对象. 如果你知道这个是string类型的数据,最后的方式就是把data放到一个array里面然后在end事件里面去拼接起来.

    var body = [];    request.on("data", function (chunk) {        body.push(chunk);    }).on("end", function () {        body = Buffer.concat(body).toString();        //此时body已经接受完整个请求 并且存于string中.        //这可能看起来弱爆了 而且很冗长.         //还好有很多module比如concat-steam和body这样的模块可以简化这些逻辑        //但是这个还是很重要,这样才能有个更好的认识关于Nodejs的http处理        //这就是你看这篇文章的原因.    });

快速看下错误处理

因为request对象是一个ReadableStream, 它也是一个EventEmitter.并且有类似的行为当有错误需要处理时.
request流发现有错误时就会emit一个error事件.如果你没有一个监听器来处理error事件,这个事件会被重新跑出,并会导致你整个程序刮掉.所以你需要在你的request流上添加一个error的监听器(即便你只是记录一下,然后继续.当然会好还是返回一些HTTP ERROR信息,后面会说到)

request.on("error", function(err){        // 输出堆栈到控制台        console.error(err.stack);     });

也有一些其他的方式来处理这些错误.但是我们必须意识到错误可能总是会发生.我们必须处理它.

看看我们现在得到了什么

现在,我们已经讲了怎么创建一个server,从请求中获取method,url,请求头和消息体.当我们把这些东西合在一起,那么应该跟下面差不多:

/** * Created by edward.gao on 26/04/2017. */var http = require("http");server = http.createServer(function (request, response) {    var method = request.method;    var url = request.url;    var headers = request.headers;    var userAgent = headers["user-agent"];    var body = [];    request.on("data", function (chunk) {        body.push(chunk);    }).on("end", function () {        body = Buffer.concat(body).toString();        //此时body已经接受完整个请求 并且存于string中.    });    request.on("error", function(err){        // 输出堆栈到控制台        console.error(err.stack);    }); }).listen(9090); // 激活server 在9090端口监听

如果我们运行这个程序,我们可以收到请求,但是没有响应他们.事实上,如果你在浏览器中访问,你的请求会超时,因为没有任何东西返回给客户端.
目前我们还没有接触到response对象. 它是一个ServerResponse的实例, 实现了WritableStream接口. 它包含了很多有用的方法来返回数据给客户端.这个我们后面会提到.

HTTP 状态码
如果你不去设置,那么response的http状态码总是200.当然并不是每个http response都按照这个来,你可以完全发送一个别的状态码,你可以通过设置statusCode属性来实现:

// 告诉客户端 页面不在response.statusCode = 404;

也有些别的更快的方法, 我们后面会看到.

设置响应头

响应头可以很方便的通过setHeader方法来实现:

    response.setHeader("Content-Type", "application/json");    response.setHeader("X-Powered-By", "nodejs");

当设置响应头时,大小写不敏感.如果重复设置,会保留最后一个值.

明确返回header数据

前面提过的设置header和状态码的方法使用的是一种比较”隐晦/含蓄”的方法. 这意味着你依赖node来在开始发送body数据之前来发送header数据.
如果你想精确的写入header数据到response流中.你可以调用writeHead方法, 它可以写入status code和headers到流中:

    response.writeHead(200, {       'Content-Type':'application/json',        'X-Powered-By':'nodejs'    });

只要你设置了header(不论隐晦的还是精确的), 你就可以准备开始发送响应数据了.

发送响应体

因为response这个对象是一个WritableStream,我们只需要用跟其他流一样的方式来写入数据到响应的body中.

    response.write('<html>');    response.write('<body>');    response.write('<h1>Hello, World!</h1>');    response.write('</body>');    response.write('</html>');    response.end();

方法end也可以在流的最后带一个可选的数据. 那么我们可以将上面的简写为:

response.end('<html><body><h1>Hello, World!</h1></body></html>');//注意到必须在写data段到body之前写入headers和状态码. 这很重要 因为http中,header在body之前.

快速看下另一个错误处理

因为response对象也可以发射error事件,有时候,你可能必须去处理它, 所以针对request对象的所有错误处理在这里同样适用.

合在一起

现在我们已经学写了如何构建http响应,让我们放在一起来跑.基于前面的例子,我们将构建一个可以发送响应的server. 我们用JSON.stringfy来格式化返回数据为json格式.

/** * Created by edward.gao on 26/04/2017. */var http = require("http");server = http.createServer(function (request, response) {    var method = request.method;    var url = request.url;    var headers = request.headers;    var userAgent = headers["user-agent"];    var body = [];    request.on("data", function (chunk) {        body.push(chunk);    }).on("end", function () {        body = Buffer.concat(body).toString();        //此时body已经接受完整个请求 并且存于string中.    });    request.on("error", function(err){        // 输出堆栈到控制台        console.error(err.stack);    });    response.statusCode = 200;    response.setHeader("Content-Type", "application/json");    var responseBody = {        headers : headers,        method : method,        url : url,        body : body    };    response.write(JSON.stringify(responseBody));    response.end(); }).listen(9090); // 激活server 在9090端口监听

Echo server 的例子

让我们来简化前面的例子,只是简单的一个echo server. request发什么,我们就回复什么. 我们需要做的只是从request中抓取数据然后写到response流中,跟我们前面做的类似:

/** * Created by edward.gao on 26/04/2017. */var http = require("http");http.createServer(function (request, response) {        var body = [];        request.on("data", function (chunk) {            body.push(chunk);        }).on("end", function () {            body = Buffer.concat(body).toString();            response.end(body);        });    }).listen(9090);

现在让我们来改进下. 我只想返回一个echo当满足如下条件时:

  • 请求方法是GET
  • URL是/echo

其他情况,我们返回一个404.

/** * Created by edward.gao on 26/04/2017. */var http = require("http");http.createServer(function (request, response) {    if (request.method === 'GET' && request.url === '/echo') {        var body = [];        request.on("data", function (chunk) {            body.push(chunk);        }).on("end", function () {            body = Buffer.concat(body).toString();            response.end(body);        });    } else {        response.statusCode = 404;        response.end();    }}).listen(9090);// 通过检查url的方式,我们实际上在做一种"路由". 还有其他路由方式可以很简单的像switch或者更复杂的框架(express). 如果你在找相关的路由试试router.

Great 现在我们来划下重点.记住request对象是一个ReadableStream, response对象是一个WritableStream. 这意味着我们可以直接通过管道间数据从一端发到另一端 这正是我们想做的echo server.

/** * Created by edward.gao on 26/04/2017. */var http = require("http");http.createServer(function (request, response) {    if (request.method === 'GET' && request.url === '/echo') {        request.pipe(response);    } else {        response.statusCode = 404;        response.end();    }}).listen(9090);

用streams就是这么简单.
还没完,正如前面多次提到的样,错误随时可能发生,我们必须处理他们.
为了处理请求流中的任何错误,我们会记录错误到stderr并返回一个400状态码来表示Bad request. 在一个真实的应用中,我们可能需要检查下这个错误,并找出正确的状态码和消息返回给客户端. 你可以参考error的文档.
在reponse中发生的错误,我们只是接入到stdout中.

/** * Created by edward.gao on 26/04/2017. */var http = require("http");http.createServer(function (request, response) {    request.on("error", function (err) {        console.error(err);        response.statusCode = 400;        response.end();    });    response.on("error", function (err) {        console.error(err);    });    if (request.method === 'GET' && request.url === '/echo') {        request.pipe(response);    } else {        response.statusCode = 404;        response.end();    }}).listen(9090);

现在我们覆盖了大多数HTTP处理中的基本概念,现在你应该可以:

  • 初始化一个 http服务器和一个请求处理函数, 然后监听一个端口
  • 从request对象中获取headers, url, method, 和消息体
  • 根据request中的url或者/和其他属性来做路由决策
  • 通过response对象来发送headers, http状态码和消息体
  • 处理在request和response中的流错误
    通过这些基本的概念, nodejs http server对于很多使用场景都可以跑起来. 也有很多其他的API. 所以请阅读完整的API- EventEmitters/Streams/HTTP.
0 0
原创粉丝点击