NodeJS 网络服务 http res

发布于 2024-09-25 04:35:05 字数 7247 浏览 25 评论 0

概览

http 模块四剑客之一的 res ,应该都不陌生了。一个 web 服务程序,接受到来自客户端的 http 请求后,向客户端返回正确的响应内容,这就是 res 的职责。

返回的内容包括:状态代码/状态描述信息、响应头部、响应主体。下文会举几个简单的例子。

var http = require('http');
var server = http.createServer(function(req, res){
    res.end('ok');
});
server.listen(3000);

例子

在下面的例子中,我们同时设置了 状态代码/状态描述信息、响应头部、响应主体,就是这么简单。

var http = require('http');

// 设置状态码、状态描述信息、响应主体
var server = http.createServer(function(req, res){
    res.writeHead(200, 'ok', {
        'Content-Type': 'text/plain'
    });
    res.end('hello');
});

server.listen(3000);

设置状态代码、状态描述信息

res 提供了 res.writeHead()、res.statusCode/res.statusMessage 来实现这个目的。

举例,如果想要设置 200/ok ,可以

res.writeHead(200, 'ok');

也可以

res.statusCode = 200;
res.statusMessage = 'ok';

两者差不多,差异点在于

  1. res.writeHead() 可以提供额外的功能,比如设置响应头部。
  2. 当响应头部发送出去后,res.statusCode/res.statusMessage 会被设置成已发送出去的 状态代码/状态描述信息。

设置响应头部

res 提供了 res.writeHead()、response.setHeader() 来实现响应头部的设置。

举例,比如想把 Content-Type 设置为 text-plain ,那么可以

// 方法一
res.writeHead(200, 'ok', {
    'Content-Type': 'text-plain'
});

// 方法二
res.setHeader('Content-Type', 'text-plain');

两者的差异点在哪里呢?

  1. res.writeHead() 不单单是设置 header。
  2. 已经通过 res.setHeader() 设置了 header,当通过 res.writeHead() 设置同名 header,res.writeHead() 的设置会覆盖之前的设置。

关于第 2 点差异,这里举个例子。下面代码,最终的 Content-Typetext/plain

var http = require('http');

var server = http.createServer(function(req, res){
    res.setHeader('Content-Type', 'text/html');
    res.writeHead(200, 'ok', {
        'Content-Type': 'text/plain'
    });
    res.end('hello');
});

server.listen(3000);

而下面的例子,则直接报错。报错信息为 Error: Can't set headers after they are sent.

var http = require('http');

var server = http.createServer(function(req, res){    
    res.writeHead(200, 'ok', {
        'Content-Type': 'text/plain'
    });
    res.setHeader('Content-Type', 'text/html');
    res.end('hello');
});

server.listen(3000);

其他响应头部操作

增、删、改、查 是配套的。下面分别举例说明下,例子太简单就直接上代码了。

// 增
res.setHeader('Content-Type', 'text/plain');

// 删
res.removeHeader('Content-Type');

// 改
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Type', 'text/html');  // 覆盖

// 查
res.getHeader('content-type');

其中略显不同的是 res.getHeader(name),name 用的是小写,返回值没做特殊处理。

res.setHeader('Content-Type', 'TEXT/HTML');
console.log( res.getHeader('content-type') );  // TEXT/HTML

res.setHeader('Content-Type', 'text/plain');
console.log( res.getHeader('content-type') );  // text/plain

此外,还有不那么常用的:

  • res.headersSent:header 是否已经发送;
  • res.sendDate:默认为 true。但为 true 时,会在 response header 里自动设置 Date 首部。

设置响应主体

主要用到 res.write() 以及 res.end() 两个方法。

res.write() API 的信息量略大,建议看下 官方文档

response.write(chunk[, encoding][, callback])

  • chunk:响应主体的内容,可以是 string,也可以是 buffer。当为 string 时,encoding 参数用来指明编码方式。(默认是 utf8)
  • encoding:编码方式,默认是 utf8。
  • callback:当响应体 flushed 时触发。(TODO 这里想下更好的解释。。。)

使用上没什么难度,只是有些注意事项:

  1. 如果 res.write() 被调用时, res.writeHead() 还没被调用过,那么,就会把 header flush 出去。
  2. res.write() 可以被调用多次。
  3. 当 res.write(chunk) 第一次被调用时,node 会将 header 信息 以及 chunk 发送到客户端。第二次调用 res.write(chunk) ,node 会认为你是要 streaming data(WTF,该怎么翻译)。。。

Returns true if the entire data was flushed successfully to the kernel buffer. Returns false if all or part of the data was queued in user memory. 'drain' will be emitted when the buffer is free again.

response.end([data][, encoding][, callback])

掌握了 res.write() 的话,res.end() 就很简单了。res.end() 的用处是告诉 nodejs,header、body 都给你了,这次响应就到这里吧。

有点像个语法糖,可以看成下面两个调用的组合。至于 callback,当响应传递结束后触发。

res.write(data, encoding);
res.end()

chunk 数据

参考这里: http://stackoverflow.com/questions/6258210/how-can-i-output-data-before-i-end-the-response

也就是说,除了 nodejs 的特性,还需要了解 HTTP 协议、浏览器的具体实现。(细思极恐)

如果是 text/html

var http = require('http');

http.createServer(function(req, res) {    
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.write('hello');

    setTimeout(function() {
        res.write(' world!');
        res.end();
    }, 2000);

}).listen(3000);

如果是 text/plain

var http = require('http');

http.createServer(function (req, res) {
    res.writeHead(200, {
        'Content-Type': 'text/plain; charset=utf-8',
        'X-Content-Type-Options': 'nosniff'
    });
    res.write('hello');

    setTimeout(function(){
        res.write('world');
        res.end()
    }, 2000);

}).listen(3000);

失败例子

var http = require('http');

var server = http.createServer(function(req, res){
    res.writeHead(200, 'ok', {
        'Content-Type': 'text/html'
    });
    res.write('hello');

    setTimeout(function(){
        res.write('world');
        res.end();
    }, 2000);
});

server.listen(3000);

超时处理

接口:response.setTimeout(msecs, callback)

关于 timeout 事件的说明,同样是言简意赅(WTF),话少信息量大,最好来个 demo TODO

If no 'timeout' listener is added to the request, the response, or the server, then sockets are destroyed when they time out. If you assign a handler on the request, the response, or the server's 'timeout' events, then it is your responsibility to handle timed out sockets.

事件 close/finish

  • close:response.end() 被调用前,连接就断开了。此时会触发这个事件。
  • finish:响应 header、body 都已经发送出去(交给操作系统,排队等候传输),但客户端是否实际收到数据为止。(这个事件后,res 上就不会再有其他事件触发)

其他不常用属性/方法

  • response.finished:一开始是 false,响应结束后,设置为 true。
  • response.sendDate:默认是 true。是否自动设置 Date 头部。(按 HTTP 协议是必须要的,除非是调试用,不然不要设置为 false)
  • response.headersSent:只读属性。响应头部是否已发送。
  • response.writeContinue():发送 HTTP/1.1 100 Continue 消息给客户端,提示说服务端愿意接受客户端的请求,请继续发送请求正文(body)。(TODO 做个 demo 啥的是大大的好)

相关链接

How can I output data before I end the response? http://stackoverflow.com/questions/6258210/how-can-i-output-data-before-i-end-the-response

8.2.3 Use of the 100 (Continue) Status http://greenbytes.de/tech/webdav/rfc2616.html

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

热情消退

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

马化腾

文章 0 评论 0

thousandcents

文章 0 评论 0

辰『辰』

文章 0 评论 0

ailin001

文章 0 评论 0

冷情妓

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文