Nodejs 响应头和请求头

70 阅读7分钟

请求头是浏览器发给服务器的"自我介绍",响应头是服务器回给浏览器的"回复说明"

响应头

服务器处理完请求后,返回给浏览器的"附加信息"。

常见响应头

响应头名称作用示例值
Content-Type返回数据的格式application/json
Content-Length返回数据的字节大小1024
Access-Control-Allow-Origin允许跨域访问的来源(CORS)http://localhost:5500
Set-Cookie设置CookiesessionId=abc123; Path=/
Cache-Control缓存策略no-cache, max-age=3600
Server服务器信息Express
Status状态码说明200 OK
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 15
Access-Control-Allow-Origin: http://localhost:5500
Cache-Control: no-cache

{"code":200}

请求头

浏览器发起请求时,自动携带的"附加信息"。

常见请求头

请求头名称作用示例值
Host请求的目标服务器localhost:3000
User-Agent浏览器身份标识Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept客户端能接收的数据格式application/json, text/html
Accept-Encoding客户端支持的压缩方式gzip, deflate, br
Content-Type请求体的数据格式(POST/PUT时)application/json
Authorization身份认证令牌Bearer eyJhbGciOiJIUzI1NiIs...
Cookie携带的Cookie信息sessionId=abc123
Origin请求来源(跨域时重要)http://localhost:5500
GET /info HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 Chrome/120.0
Accept: application/json
Origin: http://localhost:5500
Cookie: sessionId=abc123

响应头和请求头的区别

对比项请求头响应头
发送方浏览器(客户端)服务器
接收方服务器浏览器(客户端)
作用告诉服务器"我是谁、要什么"告诉浏览器"给你什么、什么规则"
谁设置浏览器自动设置,JS可修改服务器代码设置
关键字段Host、Origin、Cookie、AuthorizationContent-Type、Access-Control-Allow-Origin、Set-Cookie

在开发中,你只需要根据场景选择需要的请求头即可,不需要全部记住。比如,进行跨域请求时重点关注 Origin;发送 POST 请求时重点关注 Content-Type 和 Authorization。

响应头和跨域之间的关系

  • cors

跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种机制,用于在浏览器中实现跨域请求访问资源的权限控制。当一个网页通过 XMLHttpRequest 或 Fetch API 发起跨域请求时,浏览器会根据同源策略(Same-Origin Policy)进行限制。同源策略要求请求的源(协议、域名和端口)必须与资源的源相同,否则请求会被浏览器拒绝

右键Open with Live Server,直接打开http://127.0.0.1:5500/http-req-res-header/index.html,修改为http://localhost:5500/http-req-res-header/index.html

你用 Live Server 打开 HTML 文件时:

  • 页面地址:http://127.0.0.1:5500/index.html(或 localhost:5500)
  • 接口地址:http://localhost:3000/info

端口不同(5500 vs 3000),浏览器认为是跨域请求,所以报 CORS 错误。

为什么 Live Server 会用 5500 端口?

Live Server 是一个轻量级开发服务器,默认端口是 5500 ,用于托管静态 HTML 文件。你的页面通过它访问,而接口跑在 3000 端口,两者端口不同 → 跨域。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        fetch('http://localhost:3000/info').then(res=>{
            return res.json()
        }).then(res=>{
            console.log(res)
        })
    </script>
</body>
</html>
import express from 'express'
const app = express()
// app.use((req, res, next) => {
//     res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500')
//     next()
// })
app.get('/info', (req, res) => {
    res.json({
        code: 200
    })
})
app.listen(3000, () => {
    // console.log('http://localhost:3000')
})

image.png

发现是有报错的 根据同源策略我们看到协议一样,域名一样,但是端口不一致,端口也无法一致,会有冲突,否则就是前后端不分离的项目,前后端代码放在一起,只用一个端口,不过我们是分离的没法这么做。

这时候我们就需要后端支持一下,跨域请求资源放行

import express from 'express'
const app = express()
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500')
    next()
})
app.get('/info', (req, res) => {
    res.json({
        code: 200
    })
})
app.listen(3000, () => {
    // console.log('http://localhost:3000')
})

请求方法支持

我们服务端默认只支持 GET POST HEAD OPTIONS 请求

例如我们遵循restFul 要支持PATCH 或者其他请求

增加patch

import express from 'express'
const app = express()
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500')
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PATCH')
    next()
})
// app.get('/info', (req, res) => {
//     res.json({
//         code: 200
//     })
// })
app.patch('/info', (req, res) => {
    res.json({
        code: 200
    })
})
app.listen(3000, () => {
    console.log('http://localhost:3000')
})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // fetch('http://localhost:3000/info').then(res=>{
        //     return res.json()
        // }).then(res=>{
        //     console.log(res)
        // })

         fetch('http://localhost:3000/info',{
            method:'PATCH',
        }).then(res=>{
            return res.json()
        }).then(res=>{
            console.log(res)
        })
    </script>
</body>
</html>

在请求头加上这行即可!

res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PATCH')

要注意app.js这个文件中要加上patch请求,html文件中请求也要加上patch请求

预检请求

   fetch('http://localhost:3000/info',{
            method:'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({name: 'xmzs'})
        }).then(res=>{
            return res.json()
        }).then(res=>{
            console.log(res)
        })
import express from 'express'
const app = express()
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500')
    res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PATCH')
   // res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
    next()
})
// app.get('/info', (req, res) => {
//     res.json({
//         code: 200
//     })
// })
app.post('/info', (req, res) => {
    try {
        // 处理请求
        res.json({ code: 200 })
    } catch (error) {
        res.status(500).json({ code: 500, message: error.message })
    }
})
app.listen(3000, () => {
    console.log('http://localhost:3000')
})

发现报错了!!

image.png

因为 application/json 不属于cors 范畴需要手动支持

res.setHeader('Access-Control-Allow-Headers', 'Content-Type')

解决办法:去掉以上这行注释即可!

image.png

image.png

自定义响应头

在我们做需求的时候,可能会碰到后端自定义响应头

业务场景:传递“元数据”而非数据体

最常见的情况。后端返回了JSON/XML数据体,但想把数据体之外的状态信息(比如分页、签名)也传给前端。

  • 分页信息:返回列表数据时,把 X-Total-Count(总条数)、X-Page(当前页)放在响应头,而不是塞进JSON体里。这样前端分页组件可以直接从响应头读取,数据体保持干净。——这个放在响应头还是响应体里还是要看框架!!
  • 文件下载信息:下载文件时,自定义 X-File-Name(原始文件名)或 X-File-Size,方便前端在下载进度条或保存对话框中显示。
  • 业务状态码:有些架构不把业务错误码放在HTTP状态码(如200/500)里,而是在响应头自定义 X-Business-Code,专门表示“库存不足”“积分不够”等业务异常,让前端统一拦截处理。

什么时候该用自定义响应头,而不是放Body里?

当这个字段是“请求级别的控制信息”时,放Header;当它是“业务数据本身”时,放Body。

比如,X-Total-Count 是对这批数据的描述(控制分页组件),不是数据本身,所以放Header。而“用户名”“订单金额”是业务数据,放Body。

一个需要注意的坑

前端(尤其是浏览器中的JavaScript)只能读取部分响应头。默认暴露的只有 Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
如果你的自定义头是 X-Custom-Header,后端必须在响应头里加上:

http

Access-Control-Expose-Headers: X-Custom-Header, X-Total-Count

否则前端 getResponseHeader('X-Custom-Header') 会拿不到值,这是跨域安全策略(CORS)的限制。

好了言归正传!!

app.get('/info', (req, res) => {
    res.set('xmzs', '1')
    res.json({
        code: 200
    })
})

前端如何取呢?

fetch('http://localhost:3000/info').then(res=>{
            const headers = res.headers
            console.log(headers.get('xmzs'))
            return res.json()
        }).then(res=>{
            console.log(res)
        })

image.png

发现是null 这是因为后端没有抛出该响应头所以后端需要增加抛出的一个字段

app.get('/info', (req, res) => {
    res.set('xmzs', '1')
    res.setHeader('Access-Control-Expose-Headers', 'xmzs')
    res.json({
        code: 200
    })
})

这样就会返回1了!!

SSE技术

Server-Sent Events(SSE)是一种在客户端和服务器之间实现单向事件流的机制,允许服务器主动向客户端发送事件数据。在 SSE 中,可以使用自定义事件(Custom Events)来发送具有特定类型的事件数据。

webSocket属于全双工通讯,也就是前端可以给后端实时发送,后端也可以给前端实时发送,SSE属于单工通讯,后端可以给前端实时发送

  • express 增加该响应头text/event-stream就变成了sse event 事件名称 data 发送的数据
app.get('/sse',(req,res)=>{
    res.setHeader('Content-Type', 'text/event-stream')
    res.status(200)
    setInterval(() => {
        res.write('event: test\n')
        res.write('data: ' + new Date().getTime() + '\n\n')
    }, 1000)
})

前端接受

const sse = new EventSource('http://localhost:3000/sse')
sse.addEventListener('test', (event) => {
    console.log(event.data)
})

image.png