来源:Node.js

响应头

HTTP响应头(HTTP response headers)是在HTTP响应中发送的元数据信息,用于描述响应的特性、内容和行为

它们以键值对的形式出现,每个键值对由一个标头字段(header field)和一个相应的值组成

1
2
3
4
Access-Control-Allow-Origin:*
Cache-Control:public, max-age=0, must-revalidate
Content-Type:text/html; charset=utf-8
Date:Sat, 20 Jan 2024 09:12:39 GMT

跨域

跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种机制,用于在浏览器中实现跨域请求访问资源的权限控制

当一个网页通过XMLHttpRequest或Fetch API发起跨域请求时,浏览器会根据同源策略(Same-Origin Policy)进行限制

同源策略要求请求的源(协议、域名和端口)必须与资源的源相同,否则请求会被浏览器拒绝

假设在http://xxx.xx:11451有一个服务,那么

  • https://xxx.xx:11451不能访问,因为协议不同
  • http://yyy.yy:11451不能访问,因为域名不同
  • http://xxx.xx:1919不能访问,因为端口不同

如果需要放行跨域资源请求,需要后端增加响应头:

1
Access-Control-Allow-Origin: * | Origin

设为*代表允许所有请求源,但是不太安全,而且如果接口支持session,设为*就无法获取它

1
2
3
4
5
// *:所有请求都经过这个中间件
app.use('*', (req, res, next)=> {
res.setHeader('Access-Control-Allow-Origin', '*')
next()
})

预检请求

预检请求OPTIONS是由浏览器发起的请求,主要为了确保跨域请求的安全性,请求满足下面任意条件时会触发

  • 包含自定义的请求头字段,例如Content-Type为application/json或Authorization等
  • 请求需要在跨域环境下发送和接收凭证(例如包含cookies、HTTP认证等凭证信息)
  • 非简单请求(PATCH、PUT、DELETE、CONNECT、TRACE等)

请求头

cors默认仅支持客户端向服务器发送如下九个请求头:

  • Accept:指定客户端能够处理的内容类型
  • Accept-Language:指定客户端偏好的自然语言
  • Content-Language:指定请求或响应实体的自然语言
  • Content-Type:指定请求或响应实体的媒体类型
  • DNT (Do Not Track):指示客户端不希望被跟踪
  • Origin:指示请求的源(协议、域名和端口)
  • User-Agent:包含发起请求的用户代理的信息
  • Referer:指示当前请求的源 URL
  • Content-type:只支持application/x-www-form-urlencoded、multipart/form-data和text/plain三个值

application/json不属于cors范畴,若需支持json,则需要增加响应头:

1
Access-Control-Allow-Headers:Content-Type

请求方式

即使放行跨域,服务端默认只支持GET、POST、HEAD、OPTIONS请求,如果需要允许其他的请求,则还要增加响应头:

1
Access-Control-Allow-Methods:POST, GET, PUT, OPTIONS, DELETE, PATCH

自定义响应头

偶尔会有自定义响应头的需求,需要进行额外的设置从而抛出该响应头,如果不设置,则前端无法读取

1
Access-Control-Expose-Headers:自定义响应头名
1
2
3
4
5
6
7
app.get('/info', (req, res) => {
res.set('yjsp', '24')
res.setHeader('Access-Control-Expose-Headers', 'yjsp')
res.json({
code: 200
})
})

SSE单工通讯

Server-Sent Events(SSE)是一种在客户端和服务器之间实现单向事件流的机制,允许服务器主动向客户端发送事件数据

SSE中,可以使用自定义事件(Custom Events)发送具有特定类型的事件数据

还有个也可以通讯的webSocket,它属于全双工通讯,即前端可以给后端实时发送,后端也可以给前端实时发送

后端需增加响应头,从而开启通讯

1
Content-Type:text/event-stream

后端:

1
2
3
4
5
6
7
8
9
10
app.get('/sse',(req,res)=>{
res.setHeader('Content-Type', 'text/event-stream')
res.status(200)
setInterval(() => {
// 事件类型,默认为message
res.write('event: yajue\n')
// 数据
res.write('data: ' + new Date().getTime() + '\n\n')
}, 1000)
})

前端:

1
2
3
4
const sse = new EventSource('http://localhost:11451/sse')
sse.addEventListener('yajue', (e) => {
console.log(e.data)
})

这非常适用于实时数据大屏