请求头和响应头、CORS、预检请求
在网络通信中,尤其是在HTTP协议中,请求头和响应头分别指的是请求和响应中的元数据。它们通常以键值对的形式包含在HTTP请求和响应消息中,用来描述消息的特征和传递的信息。
一、.请求头、响应头
请求头(Request Headers) :
是客户端(如浏览器)在向服务器发送请求时附带的元数据。它们提供了关于请求的各种信息,包括但不限于:
- Host: 请求的目标主机和端口。
- User-Agent: 客户端的浏览器或应用程序的详细信息。
- Accept: 客户端能够接受的媒体类型(如text/html, application/json)。
- Authorization: 用于传递身份验证信息(如API密钥或登录凭证)。
- Cookie: 客户端发送到服务器的Cookie信息。
- Content-Type: 请求主体数据的类型(例如表单提交的数据类型)。
示例:
GET /index.html HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 Accept: text/html, application/xhtml+xml, */*
8.响应头 (Response Headers):
是服务器在响应客户端请求时附带的元数据。它们提供了关于响应的各种信息,包括但不限于:
- Content-Type: 响应主体的媒体类型(如text/html, application/json)。
- Content-Length: 响应主体的大小,以字节为单位。
- Date: 响应的日期和时间。
- Server: 服务器的软件信息。
- Set-Cookie: 用于设置服务器端发送到客户端的Cookie。
- Location: 用于重定向的URL地址。
示例:
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
Date: Wed, 20 Mar 2025 10:00:00 GMT
二、CORS(Cross-Origin Resource Sharing,跨域资源共享)
是一种浏览器技术,允许一个网站的资源(如API)能够被其他不同域名的网页请求。它是为了克服同源策略(Same-Origin Policy)限制而设计的。
1. 同源策略
同源策略是一种浏览器的安全机制,要求一个网站只能访问与其同源(协议、域名、端口都相同)下的资源。如果网站A尝试访问网站B的资源,而两个网站的源不同,浏览器会阻止这种跨域请求。
2. 跨域请求
跨域请求指的是在浏览器中发起的请求,其请求的源(协议、域名、端口)与当前页面的源不同。比如,网页https://www.example.com发起请求访问https://api.anotherdomain.com/data,这就是一个跨域请求。
3. CORS的作用
CORS允许服务器声明哪些域是被允许访问其资源的。通过设置HTTP头,服务器可以告诉浏览器,哪些跨域请求是被允许的,哪些不被允许。
4. CORS的工作原理
CORS是通过HTTP头来实现的,主要涉及以下几种头信息:
1.Access-Control-Allow-Origin:指定允许哪些域名访问资源。可以是具体的域名(如https://www.example.com),或者使用通配符*表示所有域都允许访问。
Access-Control-Allow-Origin: https://www.example.com
2.Access-Control-Allow-Methods:指定允许的HTTP方法,如GET, POST, PUT, DELETE等。
Access-Control-Allow-Methods: GET, POST, PUT
3.Access-Control-Allow-Headers:指定允许的请求头,通常在预检请求(preflight request)中使用。
Access-Control-Allow-Headers: Content-Type, Authorization
4.Access-Control-Allow-Credentials:是否允许携带身份凭证(如Cookies)。默认情况下,跨域请求不会发送Cookies,只有在该头设置为true时,浏览器才会发送身份信息。
Access-Control-Allow-Credentials: true
5.Access-Control-Max-Age:指定预检请求的结果可以缓存多长时间,以减少后续请求的开销。
Access-Control-Max-Age: 3600
5. 预检请求(Preflight Request)
对于某些跨域请求,浏览器会先发送一个OPTIONS请求,称为预检请求,以确认目标服务器是否允许跨域请求。这个预检请求会携带Access-Control-Request-Method和Access-Control-Request-Headers等头信息,告诉服务器即将发起的真实请求的类型和头部信息。如果服务器响应允许该跨域请求,浏览器才会继续发送实际的跨域请求。
编码:
发送请求
fetch('http://localhost:3000/info').then(res=>{ return res.json() }).then(res=>{ console.log(res) })
get接口
import express from 'express' const app = express() app.get('/info', (req, res) => { res.json({ code: 200 }) }) app.listen(3000, () => { console.log('http://localhost:3000') })
协议一样,域名一样,但是端口不一致
这时候我们就需要后端支持一下,跨域请求资源放行
Access-Control-Allow-Origin: * | Origin
全部放开会导致安全问题,所以可以开放白名单
//增加以下响应头 允许localhost 5500 访问 app.use('*',(req,res,next)=>{ res.setHeader('Access-Control-Allow-Origin','http://localhost:5500') //允许localhost 5500 访问 next() })
PATCH
请求app.patch('/info', (req, res) => { res.json({ code: 200 }) })
发送
fetch('http://localhost:3000/info',{ method:'PATCH', }).then(res=>{ return res.json() }).then(res=>{ console.log(res) })
报错:
服务端默认只支持 GET POST HEAD OPTIONS 请求
设置:
res.setHeader( "Access-Control-Allow-Methods", "GET, POST, PATCH, OPTIONS, PUT, DELETE" );
继续发送还是报错
需要在服务端
//允许携带自定义请求头content-type res.setHeader("Access-Control-Allow-Headers", "content-type");
三、预检请求(Preflight Request)
1. 预检请求的背景
当浏览器发起跨域请求时,特别是使用了某些特定的HTTP方法(如PUT、DELETE)或自定义请求头时,浏览器为了安全考虑,会首先发送一个预检请求,确保目标服务器允许该跨域请求。这是浏览器的一种安全机制,目的是避免潜在的恶意跨站请求。
2. 预检请求的工作原理
预检请求是通过OPTIONS方法发送的,通常包含一些特殊的头部信息,用于告知服务器即将发起的实际请求的类型和相关信息。
1.请求头:
- Access-Control-Request-Method: 指定实际请求将使用的HTTP方法(如GET, POST, PUT等)。
- Access-Control-Request-Headers: 指定实际请求中将使用的自定义头部(如Authorization、Content-Type等)。
- Origin: 发送请求的源(即协议、域名和端口)。
2.响应头:
- Access-Control-Allow-Methods: 服务器响应,指定允许的HTTP方法(如GET, POST等)。
- Access-Control-Allow-Headers: 服务器响应,指定允许的自定义请求头。
- Access-Control-Allow-Origin: 指定哪些源可以访问资源。
- Access-Control-Allow-Credentials: 指定是否允许发送凭据(如Cookies)。
- Access-Control-Max-Age: 服务器响应,指定预检请求的结果可以缓存多长时间。
3. 预检请求的例子
假设客户端(浏览器)发起一个跨域请求,使用了PUT方法,并且添加了自定义头部X-Custom-Header,浏览器会首先发送一个预检请求来询问服务器是否允许这个请求。
客户端发出的预检请求(OPTIONS):
OPTIONS /data HTTP/1.1 Host: api.example.com Origin: https://www.example.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header
服务器的响应:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://www.example.com Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Access-Control-Max-Age: 86400
如果服务器返回的响应包含允许的头部信息,浏览器会继续发起实际的请求;如果响应中未允许某些请求方法或头部,浏览器会阻止实际请求的发送。
4. 什么时候会触发预检请求
并不是所有的跨域请求都会触发预检请求。预检请求通常在以下情况触发:
- 请求方法是非简单方法,例如PUT、DELETE、PATCH等 (GET、POST、HEAD是简单方法)。
- 请求包含自定义头部,例如Authorization、X-Custom-Header等。
- 请求使用了带有内容的请求体(如Content-Type为application/json,但不是application/x-www-form-urlencoded、multipart/form-data或text/plain这三种简单类型)。
5. 简化的跨域请求
如果请求符合简单请求的条件(即使用简单方法且不带自定义头部),则浏览器不会发送预检请求,直接发送实际请求。简单请求的条件如下:
- 请求方法是:GET、POST、HEAD。
- 请求头部仅包含:Accept、Accept-Language、Content-Language、Content-Type(并且Content-Type的值为application/x-www-form-urlencoded、multipart/form-data、text/plain这三种之一)。
四、自定义响应头
后端
app.get('/info', (req, res) => { res.set('zxd', '1') res.json({ code: 200 }) })
前端
fetch('http://localhost:3000/info').then(res=>{ const headers = res.headers console.log(headers.get('zxd')) //读取自定义响应头 return res.json() }).then(res=>{ console.log(res) })
这样前端其实获取不到
后台还需要设置
res.setHeader('Access-Control-Expose-Headers', 'zxd')
五、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"); // SSE setInterval(() => { res.write('event:zxd\n') // 默认是massage 自定义为zxd res.write(`data: ${new Date().toLocaleString()}\n\n`); }, 1000); });
前端
// //默认叫message // sse.addEventListener("message", (e) => { // console.log(e.data) // }) //自定义事件名称 sse.addEventListener("zxd", (e) => { console.log(e.data) })