什么是 HTTP 请求中的 preflight 类型请求
什么是 HTTP 请求中的 preflight 类型请求
在浏览器的 HTTP 请求中,当我们使用 fetch API 或者 XMLHttpRequest 来进行跨域请求时,浏览器有时会发送一种称为 Preflight 的请求。这种请求是浏览器在实际发送跨域请求前,先与目标服务器进行的一次 “探测” 请求,以确认服务器是否允许这样的请求方式。Preflight 请求的存在是为了保障浏览器的安全性,确保跨域请求不会在没有服务器允许的情况下进行。
跨域资源共享(CORS)机制规定了当浏览器发起跨域请求时,某些条件下需要发送 Preflight 请求。这种探测性的请求使用 OPTIONS 方法发出,目标是向服务器询问,客户端接下来想要发送的实际请求是否被允许。
Preflight 请求的触发条件
不是所有的跨域请求都会触发 Preflight 请求,浏览器会根据请求的类型和头部信息来决定是否需要预检。Preflight 请求通常在以下几种情况下触发:
当请求方法不是
GET、POST或HEAD,例如PUT、DELETE。当请求包含非标准的 HTTP 头部字段,比如自定义的
Authorization头部,或者Content-Type不是application/x-www-form-urlencoded、multipart/form-data、text/plain。请求中涉及跨域资源时,尤其是涉及到敏感的操作时,浏览器会通过
Preflight请求来确保服务器允许这些操作。
这种设计的初衷是为了防止跨域请求滥用,尤其是在涉及敏感数据的场景下,确保浏览器与服务器之间的交互安全。
Preflight 请求的流程
当浏览器决定某个跨域请求需要进行 Preflight,它会先向目标服务器发出一个 OPTIONS 请求,携带一些必要的头部信息,如 Access-Control-Request-Method 和 Access-Control-Request-Headers,用于告知服务器,客户端即将发送的请求的具体方法和头部信息。服务器通过响应来告知浏览器,是否允许这样的请求。
请求示例:
假设我们要向 https://api.example.com/data 发送一个跨域的 PUT 请求,并且需要携带自定义头部 X-Custom-Header。在实际发出 PUT 请求之前,浏览器会自动生成如下的 Preflight 请求:
OPTIONS /data HTTP/1.1
Host: api.example.com
Origin: https://client.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
其中,Access-Control-Request-Method 头部用来告诉服务器接下来会使用 PUT 方法,而 Access-Control-Request-Headers 则表明请求中会携带 X-Custom-Header 这个自定义头部。
服务器响应:
服务器接收到 Preflight 请求后,必须返回一个响应来告诉浏览器是否允许此类请求:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://client.example.com
Access-Control-Allow-Methods: GET, PUT, POST, DELETE
Access-Control-Allow-Headers: X-Custom-Header
服务器通过 Access-Control-Allow-Origin 来指定哪些源是允许访问的(这里是 https://client.example.com),通过 Access-Control-Allow-Methods 来列出允许的 HTTP 方法,并通过 Access-Control-Allow-Headers 来列出允许的头部字段。
Preflight 请求的使用场合
Preflight 请求主要用于跨域场景下,特别是那些涉及到更复杂请求的场合,比如非 GET 或 POST 方法,或者请求中包含了额外的自定义头部。
常见的使用场景包括:
RESTful API 请求 :当前端应用需要与其他域名下的 REST API 进行交互时,尤其是对资源进行
PUT或DELETE操作时,往往会触发Preflight请求。这些操作可能会修改服务器上的数据,因此需要确保安全。例如,一个前端应用需要向远程服务器发送数据更新请求,使用
PUT方法更新用户信息。在这种场景下,Preflight请求就会确保目标服务器允许跨域的PUT请求。上传文件的操作 :在表单上传文件时,如果使用
fetchAPI 或XMLHttpRequest并携带了非标准的头部,比如自定义的认证信息,通常会触发Preflight请求。浏览器需要确保服务器允许上传操作以及这些自定义的头部字段。自定义认证头部的请求 :很多应用在发起跨域请求时,需要在头部中携带如
Authorization或Token的自定义认证信息。由于这些头部字段并非标准字段,浏览器会先发送Preflight请求来探测服务器是否允许使用这些自定义头部。
Preflight 请求的实际案例
在实际开发中,有一个典型的例子是前端应用需要向第三方服务发送请求并带有认证信息。这种场景下的跨域请求常常会触发 Preflight 请求。
假设我们开发了一个电子商务应用,这个应用的后端服务托管在 https://api.shop.com,而前端页面托管在 https://shop.com。用户在购物时,前端需要向后端发送带有用户身份认证的请求,如以下场景:
- 用户在购物车页面点击结账,前端应用需要向后端 API 发送包含用户认证信息的请求,以确认用户是否已登录,且是否有购买权限。
- 请求方法为
POST,同时头部中带有Authorization: Bearer token123来验证用户身份。
此时,由于 Authorization 头部是非标准字段,且前端和后端的域名不同,这个请求会触发 Preflight 检查。
请求步骤:
Preflight 请求 :
浏览器会自动先发送一个OPTIONS请求,探测服务器是否允许发送带有Authorization头部的跨域请求:OPTIONS /checkout HTTP/1.1 Host: api.shop.com Origin: https://shop.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Authorization服务器响应 :
后端服务器接收到该请求后,会检查是否允许该跨域请求,并返回允许的结果:HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://shop.com Access-Control-Allow-Methods: POST Access-Control-Allow-Headers: Authorization响应中,服务器明确告知浏览器允许该域名
https://shop.com发起POST请求,并且允许使用Authorization头部。实际请求 :
在确认服务器允许跨域请求后,浏览器会继续发送实际的POST请求,包括认证信息:POST /checkout HTTP/1.1 Host: api.shop.com Origin: https://shop.com Authorization: Bearer token123
使用 Preflight 请求的优化
尽管 Preflight 请求保障了安全性,但在一些频繁的跨域请求场景下,这会带来额外的网络开销。为此,可以采用一些优化策略:
服务器缓存
Preflight响应 :通过在响应中设置Access-Control-Max-Age头部,服务器可以告知浏览器在一定时间内不需要重复进行Preflight请求。这能显著减少不必要的OPTIONS请求,提升应用的性能。例如,服务器可以返回这样的响应,告知浏览器在未来 10 分钟内不需要重新发起
Preflight请求:HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://shop.com Access-Control-Allow-Methods: POST Access-Control-Allow-Headers: Authorization Access-Control-Max-Age: 600减少复杂的请求 :避免不必要的自定义头部字段,或者尽量使用简单的
GET、POST请求,能够有效减少Preflight请求的触发。对于一些轻量级的操作,使用标准的请求方法和头部可以避免Preflight请求,从而提升效率。
结语
Preflight 请求作为 CORS 机制的一部分,主要作用是确保跨域请求的安全性,尤其是在涉及非标准请求时。通过 Preflight 请求,浏览器与服务器能够就请求的合法性达成共识,保护用户的数据安全。在实际应用中,理解 Preflight 请求的工作原理,并在合适的场景下进行优化,能够大大提升 Web 应用的性能和用户体验。