http模块中的writeHead

writeHead

response.writeHead(statusCode[, statusMessage][, headers])
statusMessage 好像没什么用,一般用不到。
返回对 ServerResponse 的引用,以便可以链式调用。

const body = 'hello world';
response
  .writeHead(200, {
    'Content-Length': Buffer.byteLength(body),
    'Content-Type': 'text/plain'
  })
  .end(body);

如果在调用writeHead之前调用了 response.write() 或 response.end(),会报错。

net::ERR_INVALID_CHUNKED_ENCODING

当使用 response.setHeader() 设置响应头时,则与传给 response.writeHead() 的任何响应头合并,且 response.writeHead() 的优先。

res.setHeader('X-Foo', 'bar');
res.writeHead(200, { 'X-Foo': 'bar11' });
console.log(res.getHeader('Content-Type') ); // bar11

调用writeHead之后调用setHeader,报错:

Cannot set headers after they are sent to the client
// 在源代码中有如下的定义,这里是报错的地方
OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
  if (this._header) {
    throw new ERR_HTTP_HEADERS_SENT('set');
  }
}

如果调用此方法并且尚未调用 response.setHeader(),则直接将提供的响应头值写入网络通道而不在内部进行缓存,响应头上的 response.getHeader() 将不会产生预期的结果。

response.writeHead(200, { 'Content-Type': 'text/plain' });
console.log(response.getHeader('Content-Type') ); // undefined
// 没有内部缓存导致获取不到刚刚设置的'content-type'

对比先调用response.setHeader,再调用writeHead:

res.setHeader('X-Foo', 'bar');
res.writeHead(200, { 'Content-Type': 'text/plain' });
console.log(res.getHeader('Content-Type') ); // 'text/plain'
// 可以获取到刚刚设置的content-type

如果需要渐进的响应头填充以及将来可能的检索和修改,则改用 response.setHeader()。

// 返回 content-type = text/plain
const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.setHeader('X-Foo', 'bar');
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('ok');
});

之前在遇到一个问题:

代码中只是重新定义了res.writeHead,在向客户端返回数据的时候会自动调用res.writeHead,问题在于代码中并没有调用writeHead ?

var http = require('http')
var server  = http.createServer(function (req,res) {
    var _ = res.writeHead
    res.writeHead = function(...arg){
        console.log(arg);
        _.call(this,arg)
    }
    res.write('adfasdfsf') // 调用write会触发writeHead
    res.end()
})
server.listen(10899,function () {
    console.log(121212);
})

通过断点跟踪发现有如下的逻辑链条:

ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype);// 原型继承,response上面的部分方法继承自OutgoingMessage

// write方法,当调用response.write的时候就是调用的这里
OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
  const ret = write_(this, chunk, encoding, callback, false);
  if (!ret)
    this[kNeedDrain] = true;
  return ret;
};

function write_(msg, chunk, encoding, callback, fromEnd) {
  if (!msg._header) {
    msg._implicitHeader();
  }
}

// 调用writeHead,所以只要执行write方法就会调用writeHead方法	
ServerResponse.prototype._implicitHeader = function _implicitHeader() {
  this.writeHead(this.statusCode);
};

ServerResponse.prototype.writeHead = writeHead;
function writeHead(statusCode, reason, obj) {}

同样在在调用response.end的时候也有类似的处理:

ServerResponse.prototype.end继承自OutgoingMessage.prototype.end

OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
 ......
  if (chunk) {
 		.........
    write_(this, chunk, encoding, null, true);
  } else if (this.finished) {
    ......
  } else if (!this._header) {
    this._contentLength = 0;
    this._implicitHeader();
  }
}
// 如果传入了第一个参数,调用write_,接下来就是上面的步骤
// 如果没有传入参数,并且没有header,直接调用了_implicitHeader,其实就是this.writeHead(this.statusCode);
posted @ 2020-08-30 21:55  空山与新雨  阅读(1160)  评论(0编辑  收藏  举报