[问题]HttpServer中的Response的writeHead和setHeader方法有什么本质区别
发布于 6 年前 作者 a272121742 31614 次浏览 最后一次编辑是 2 年前

各位大神晚上好,这个问题是我使用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会报错),小弟拜谢!

6 回复

真心求解,各位大神飘过留言啊。。。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的区别,通过查看文档与编码测试,发现如下区别:
  • 前者只能设置单个头的信息,后者可以设置多个
  • 后者可以设置状态消息与状态文本,前者待定(此处我不确定
  • 后者设置的信息将与前者设置的信息合并,一起被发送给客户端
回到顶部