请求头和响应头、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()
})

 

 

2、当接口需要遵循resful 例如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"
  );

继续发送还是报错

 

cors 的content-type 默认支持 application/x-www-form-urlencoded    multipart/form-data text/plain

需要在服务端

//允许携带自定义请求头content-type
  res.setHeader("Access-Control-Allow-Headers", "content-type");

 

三、预检请求(Preflight Request)

是一种特殊类型的HTTP请求,通常用于跨域资源共享(CORS)的场景中。预检请求的主要作用是让浏览器在发送实际的跨域请求之前,首先通过发送一个OPTIONS请求来询问目标服务器是否允许当前的跨域请求。
 

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)
        })

 

 

 

posted @ 2025-03-20 11:57  蜗牛般庄  阅读(228)  评论(0)    收藏  举报
Title
页脚 HTML 代码