[问题]Nodejs中HttpServer的Response的writeHead和setHeader方法有什么本质区别

[问题]Nodejs中HttpServer的Response的writeHead和setHeader方法有什么本质区别

各位大神晚上好,这个问题是我使用rrestjs时候偶然发现的,网上也没查阅到相关资料,写了如下的issues提交给老吴了,以下是发现这个问题的被坑、寻坑、填坑的过程。

当我实现了旁路业务和核心业务分离之后,随之而来的就遇到一些列的麻烦和问题,程序会莫名其妙的告诉我Error: Can't set headers after they are sent.,通过一个案例测试出来,案例如下:

var http = require('http');
var rrest = require('rrestjs');
var server = http.createServer(function(req,res){
	if(true){
		res.send('123');
	}
	res.send('321');
}).listen(80);

当我打开浏览器输入127.0.0.1的时候,页面是显示123,但是后台报错

http.js:644
    throw new Error('Can\'t set headers after they are sent.');
          ^
Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:644:11)
    at RestZlib.send (D:\projects\eclipse\node_modules\rrestjs\lib\RestZlib.js:20:7)
    at Gzip.onEnd (zlib.js:153:5)
    at Gzip.EventEmitter.emit (events.js:85:17)
    at zlib.js:349:10
    at Zlib.callback (zlib.js:439:13)

这样的错误我在原生的写法里没有遇到过,于是我开始用原生写法如下案例测试

var http = require('http');
var server = http.createServer(function(req,res){
	if(true){
		res.write(JSON.stringify({a:123}));
		res.end();
	}
	res.write(JSON.stringify({a:321}));
	res.end();
}).listen(80);

浏览器显示{"a":123} 好的,这没报错,这不是我所希望的,我反而希望他报错,于是我看了下之前的报错提示Can't set headers after they are sent.,翻译过来就是他们已经发送,您无法设置headers,难道发送后就不能设置headers了吗?于是我改进了原生代码,是这样的

var http = require('http');
var server = http.createServer(function(req,res){
	if(true){
		res.writeHeader(200, {'Content-Type':'text/javascript;charset=UTF-8'})
		res.write(JSON.stringify({a:123}));
		res.end();
	}
	res.writeHeader(200, {'Content-Type':'text/javascript;charset=UTF-8'})
	res.write(JSON.stringify({a:321}));
	res.end();
}).listen(80);

结果是浏览器依然有显示后台也没报错,我很不甘心,于是我想,是不是要在发送后,设置点不一样的headers呢?继续改进原生的代码

var http = require('http');
var server = http.createServer(function(req,res){
	if(true){
		res.writeHeader(200, {'Content-Type':'text/javascript;charset=UTF-8'})
		res.write(JSON.stringify({a:123}));
		res.end();
	}
	res.writeHead(200, {'Content-Length': 1024,'Content-Type': 'text/plain;charset=GBK' });
	res.write(JSON.stringify({a:321}));
	res.end();
}).listen(80);

node似乎很智能啊,这样依然没有报错,我依然不甘心,我把关键字锁定在了set上,我想我是不是该调用setHeader方法呢,于是我这样

var http = require('http');
var server = http.createServer(function(req,res){
	if(true){
		res.setHeader('Content-Type','text/javascript;charset=UTF-8');
		res.write(JSON.stringify({a:123}));
		res.end();
	}
	res.setHeader('Content-Length',1024);
	res.setHeader('Content-Type','text/html;charset=GBK');
	res.write(JSON.stringify({a:321}));
	res.end();
}).listen(80);

好吧,其实我已经精疲力尽的写到这了,我真希望这个错误能重现,果然出现了,和rrestjs框架所报的错误一模一样,因此呢…… 我的建议就是将setHeader都替换为writeHeader,这样他不会报错,而且一旦发送出去req是不会重发(后面的依然会执行,但不会重发)。 另外我想知道setHeader和writeHead的本质区别

问题是已经发给老吴了,也发给大家看看,希望大家帮忙找找相关文案,迫切想知道这2个方法的本质区别(包括为什么writeHead重复设置不会报错,而setHeader会报错),小弟拜谢!


8 回复

Node.js 中 HttpServer 的 Response 的 writeHead 和 setHeader 方法有什么本质区别

背景

在使用 Node.js 编写 HTTP 服务器时,我们经常会遇到 res 对象的 writeHeadsetHeader 方法。这两个方法都可以用来设置响应头,但在实际使用中可能会出现不同的行为。本文将详细解释这两个方法的区别,并提供示例代码。

writeHead 方法

writeHead 方法用于一次性设置响应的状态码和多个头部信息。它通常在响应体之前调用,以便在发送响应体之前设置必要的头部信息。

示例代码:

const http = require('http');

const server = http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
});

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,writeHead 设置了状态码为 200Content-Type 头部信息为 text/plain。一旦调用了 writeHead,后续再调用 writeHeadsetHeader 都不会报错,因为头部信息已经被设置且响应已经发送。

setHeader 方法

setHeader 方法用于单独设置单个头部信息。它可以在响应体之前或之后调用,但一旦调用了 end 方法,再调用 setHeader 就会抛出 Error: Can't set headers after they are sent. 错误。

示例代码:

const http = require('http');

const server = http.createServer((req, res) => {
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World\n');
    
    // 这行代码会导致错误,因为头部已经发送
    // res.setHeader('Content-Length', 1024); // Uncomment to see the error
});

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,setHeader 设置了 Content-Type 头部信息为 text/plain。如果在调用 end 之后再调用 setHeader,则会抛出错误。

总结

  • writeHead 用于一次性设置多个头部信息,并且一旦调用就不会再报错。
  • setHeader 用于单独设置单个头部信息,但必须在调用 end 之前完成设置,否则会抛出错误。

希望这些示例和解释能帮助你理解 writeHeadsetHeader 方法之间的区别。


真心求解,各位大神飘过留言啊。。。enter image description here

很好理解吧。。。http协议不是先发头的么。。。

从api上开,setHeader是可以多次设置http头部的,writeHeader只能设置一次,将状态吗,还有header一次性设置好。功能不同

大叔,你这个找问题的方式不对吧,错误提示: throw new Error(‘Can’t set headers after they are sent.’);

这不是告诉你headers 已经被sent过了嘛,你应该查一下send是否允许重复调用呢? 还写这么多测试代码,直接api不就完事了啊

http协议就是这样的, 一旦你开始发送html实体了, 你再setHeader一定会报错的, 人家都提示你了 无语…

  1. 楼主测试的方法不对,问题出在向客户端发送数据后,仍然尝试更改header,所以引发错误,与是用response.setHeader 还是 response.writeHead无关
  2. response.setHeaderresponse.writeHead的区别,通过查看文档与编码测试,发现如下区别:
  • 前者只能设置单个头的信息,后者可以设置多个
  • 后者可以设置状态消息与状态文本,前者待定(此处我不确定
  • 后者设置的信息将与前者设置的信息合并,一起被发送给客户端

res.writeHead()res.setHeader() 方法的主要区别在于它们在 HTTP 响应中的使用时机和行为。

  • res.writeHead(statusCode, [statusMessage], [headers]) 方法用于一次性设置状态码和响应头。如果多次调用 writeHead,只有第一次调用的有效,后续的调用会被忽略。

  • res.setHeader(key, value) 方法可以用来单独设置响应头。如果多次调用 setHeader 设置同一个键,后面的值会覆盖前面的值。

示例代码:

const http = require('http');

const server = http.createServer((req, res) => {
    // 使用 writeHead
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.write('First response');
    res.end();

    // 使用 setHeader
    res.setHeader('Content-Type', 'text/html');
    res.write('Second response');
    res.end();
});

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,writeHead 只会设置一次状态码和响应头,而 setHeader 可以在不同的地方设置响应头,但每次调用都会覆盖之前的设置。

回到顶部