NodeJS Guide - 走进HTTP

这个文章翻译自-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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值