Node | res.setHeader()和res.writeHead()的区别

📋 文章摘要:

本文详细解析了 Node.js 中 res.setHeader()res.writeHead() 的核心区别与使用场景:

  1. 本质区别setHeader 是内存中的准备操作,可多次修改;writeHead 是立即发送操作,一旦调用即锁定头部
  2. 协作顺序 :必须先 setHeaderwriteHead,反之会报错
  3. 使用建议 :动态头部用 setHeader,固定头部用 writeHead,流式响应必须先用 writeHead
  4. 核心原则setHeader 用于"幕后准备",writeHead 用于"前台开演"

在 Node.js 原生 http 模块中,res.setHeader()res.writeHead() 都用于设置响应头,但它们的核心本质区别 在于:setHeader 是"写在内存里"的准备操作,而 writeHead 是"真正发出去"的发送操作。

你可以把响应头想象成一封即将寄出的信:

  • res.setHeader() :像往信封上贴便签(准备信息),可以随时贴、反复贴,但只要没交给邮局,就还能修改。
  • res.writeHead() :像把信封递进邮筒并盖上邮戳(状态码 + 头部),一旦投递,信封内容(头部)就锁死了,无法再更改。

1. 核心区别详解

对比维度 res.setHeader(name, value) res.writeHead(statusCode[, statusMessage][, headers])
主要职责 仅设置/修改单个响应头的值 一次性发送状态码和所有响应头(开启响应流)
是否处理状态码 ❌ 不处理,只关心 Header 字段 ✅ 必须传入状态码(如 200、404)
调用次数 可以多次调用,分别设置不同字段 通常只能调用一次(因为调用后头部就发送了)
发送时机(关键) 只修改内存中的 _headers 对象,不立即发送 。头部会在第一次调用 res.write()res.end()隐式发送 立即 将状态行和头部数据写入网络套接字(显式发送),标志着响应头阶段结束。
调用后的影响 调用后仍可通过再次 setHeader 覆盖同名头部 调用后,若再调用 setHeader 会抛出 Error: Cannot set headers after they are sent

2. 二者的协作顺序(重要场景)

它们并非互斥,经常搭配使用,但顺序决定结果

  • setHeader,后 writeHead(推荐)

    setHeader 设置的头部会暂存在内存中,当调用 writeHead 时,会将所有已设置的头部 (包括之前的)连同状态码一起发送出去。此时 writeHead 中的 headers 对象如果与之前同名,会覆盖之前的设置。

    javascript 复制代码
    res.setHeader('Content-Type', 'text/plain');
    res.setHeader('X-Custom', 'Hello');
    res.writeHead(200, { 'X-Custom': 'World' }); // 最终 X-Custom 为 'World',Content-Type 仍为 'text/plain'
  • writeHead,后 setHeader(报错)

    一旦 writeHead 执行完毕,头部已被发出,此时再调用 setHeader 会直接触发 Node.js 的 ERR_HTTP_HEADERS_SENT 错误。


3. 实战选择建议

场景 推荐做法
需要动态添加多个头部 (如根据逻辑判断是否加 Cache-Control res.setHeader() 逐一设置,最后用 res.writeHead(200) 发送状态码(无头部参数),或直接 res.end() 让头部自动隐式发送。
需要设置状态码,且头部固定不变(如重定向) 直接用 res.writeHead(302, { Location: '/new' }) 一步到位,代码最简洁。
流式传输大文件(需先发完头部再慢慢写 body) 务必先用 res.writeHead() 显式发送头部,再用 res.write() 分块传输数据,确保客户端提前知晓头部信息。

💎 一句话总结

res.setHeader() 是"幕后准备"(可多次修改,最后统一发送),而 res.writeHead() 是"前台开演"(立即发送状态码+头部,一旦开演就谢绝改词)。

日常开发中,如果不需要严格区分发送时机,只用 res.setHeader() + res.end() 或直接 res.writeHead() 一步到位均可;但如果涉及流式响应 ,则必须先用 writeHead 发送头部,再用 write 推送数据体。