摘要:options预检请求是干嘛的?options请求一定会在post请求之前发送吗?前端或者后端开发需要手动干预这个预检请求吗?不用文档定义堆砌名词,从前后端角度单独分析,大白话带你了解!

本文分享自华为云社区《从前后端的角度分析options预检请求——打破前后端联调的理解障碍》,作者: 砖业洋__ 。

options预检请求是干嘛的?options请求一定会在post请求之前发送吗?前端或者后端开发需要手动干预这个预检请求吗?不用文档定义堆砌名词,从前后端角度单独分析,大白话带你了解!

从前端的角度看options——post请求之前一定会有options请求?信口雌黄!

你是否经常看到这种跨域请求错误?

这是因为服务器不允许跨域请求,这里会深入讲一讲OPTIONS请求。

只有在满足一定条件的跨域请求中,浏览器才会发送OPTIONS请求(预检请求)。这些请求被称为“非简单请求”。反之,如果一个跨域请求被认为是“简单请求”,那么浏览器将不会发送OPTIONS请求。

简单请求需要满足以下条件:

  1. 只使用以下HTTP方法之一:GET、HEAD或POST。
  2. 只使用以下HTTP头部:Accept、Accept-Language、Content-Language、Content-Type。
  3. Content-Type的值仅限于:application/x-www-form-urlencoded、multipart/form-data或text/plain。

如果一个跨域请求不满足以上所有条件,那么它被认为是非简单请求。对于非简单请求,浏览器会在实际请求(例如PUT、DELETE、PATCH或具有自定义头部和其他Content-Type的POST请求)之前发送OPTIONS请求(预检请求)。

举个例子吧,口嗨半天是看不懂的,让我们看看 POST请求在什么情况下不发送OPTIONS请求

提示:当一个跨域POST请求满足简单请求条件时,浏览器不会发送OPTIONS请求(预检请求)。以下是一个满足简单请求条件的POST请求示例:

// 使用Fetch API发送跨域POST请求
fetch("https://example.com/api/data", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "key1=value1&key2=value2"
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));

在这个示例中,我们使用Fetch API发送了一个跨域POST请求。请求满足以下简单请求条件:

  1. 使用POST方法。
  2. 使用的HTTP头部仅包括Content-Type。
  3. Content-Type的值为"application/x-www-form-urlencoded",属于允许的三种类型之一(application/x-www-form-urlencoded、multipart/form-data或text/plain)。

因为这个请求满足了简单请求条件,所以浏览器不会发送OPTIONS请求(预检请求)。

我们再看看什么情况下POST请求之前会发送OPTIONS请求,同样用代码说明,进行对比

提示:在跨域请求中,如果POST请求不满足简单请求条件,浏览器会在实际POST请求之前发送OPTIONS请求(预检请求)。

// 使用Fetch API发送跨域POST请求
fetch("https://example.com/api/data", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Custom-Header": "custom-value"
},
body: JSON.stringify({
key1: "value1",
key2: "value2"
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));

在这个示例中,我们使用Fetch API发送了一个跨域POST请求。请求不满足简单请求条件,因为:

  1. 使用了非允许范围内的Content-Type值("application/json" 不属于 application/x-www-form-urlencoded、multipart/form-data或text/plain)。
  2. 使用了一个自定义HTTP头部 “X-Custom-Header”,这不在允许的头部列表中。

因为这个请求不满足简单请求条件,所以在实际POST请求之前,浏览器会发送OPTIONS请求(预检请求)。

你可以按F12直接在Console输入查看Network,尽管这个网址不存在,但是不影响观察OPTIONS请求,对比一下我这两个例子。

总结:当进行非简单跨域POST请求时,浏览器会在实际POST请求之前发送OPTIONS预检请求,询问服务器是否允许跨域POST请求。如果服务器不允许跨域请求,浏览器控制台会显示跨域错误提示。如果服务器允许跨域请求,那么浏览器会继续发送实际的POST请求。而对于满足简单请求条件的跨域POST请求,浏览器不会发送OPTIONS预检请求。

后端可以通过设置Access-Control-Max-Age来控制OPTIONS请求的发送频率。OPTIONS请求没有响应数据(response data),这是因为OPTIONS请求的目的是为了获取服务器对于跨域请求的配置信息(如允许的请求方法、允许的请求头部等),而不是为了获取实际的业务数据,OPTIONS请求不会命中后端某个接口。因此,当服务器返回OPTIONS响应时,响应中主要包含跨域配置信息,而不会包含实际的业务数据

本地调试一下,前端发送POST请求,后端在POST方法里面打断点调试时,也不会阻碍OPTIONS请求的返回

从后端的角度看options——post请求之前一定会有options请求?胡说八道!

在配置跨域时,服务器需要处理OPTIONS请求,以便在响应头中返回跨域配置信息。这个过程通常是由服务器的跨域中间件(Node.js—Express框架的cors中间件、Python—Flask框架的flask_cors扩展)或过滤器(Java—SpringBoot框架的跨域过滤器)自动完成的,而无需开发人员手动处理。

以下是使用Spring Boot的一个跨域过滤器,供参考

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
public CorsConfig() {
}
@Bean
public CorsFilter corsFilter() {
// 1. 添加cors配置信息
CorsConfiguration config = new CorsConfiguration();
// Response Headers里面的Access-Control-Allow-Origin: http://localhost:8080
config.addAllowedOrigin("http://localhost:8080");
// 其实不建议使用*,允许所有跨域
config.addAllowedOrigin("*");
// 设置是否发送cookie信息,在前端也可以设置axios.defaults.withCredentials = true;表示发送Cookie,
// 跨域请求要想带上cookie,必须要请求属性withCredentials=true,这是浏览器的同源策略导致的问题:不允许JS访问跨域的Cookie
/**
* withCredentials前后端都要设置,后端是setAllowCredentials来设置
* 如果后端设置为false而前端设置为true,前端带cookie就会报错
* 如果后端为true,前端为false,那么后端拿不到前端的cookie,cookie数组为null
* 前后端都设置withCredentials为true,表示允许前端传递cookie到后端。
* 前后端都为false,前端不会传递cookie到服务端,后端也不接受cookie
*/
// Response Headers里面的Access-Control-Allow-Credentials: true
config.setAllowCredentials(true);
// 设置允许请求的方式,比如get、post、put、delete,*表示全部
// Response Headers里面的Access-Control-Allow-Methods属性
config.addAllowedMethod("*");
// 设置允许的header
// Response Headers里面的Access-Control-Allow-Headers属性,这里是Access-Control-Allow-Headers: content-type, headeruserid, headerusertoken
config.addAllowedHeader("*");
// Response Headers里面的Access-Control-Max-Age:3600
// 表示下回同一个接口post请求,在3600s之内不会发送options请求,不管post请求成功还是失败,3600s之内不会再发送options请求
// 如果不设置这个,那么每次post请求之前必定有options请求
config.setMaxAge(3600L);
// 2. 为url添加映射路径
UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
// /**表示该config适用于所有路由
corsSource.registerCorsConfiguration("/**", config);
// 3. 返回重新定义好的corsSource
return new CorsFilter(corsSource);
}
}

这里setMaxAge方法来设置预检请求(OPTIONS请求)的有效期,当浏览器第一次发送非简单的跨域POST请求时,它会先发送一个OPTIONS请求。如果服务器允许跨域,并且设置了Access-Control-Max-Age头(设置了setMaxAge方法),那么浏览器会缓存这个预检请求的结果。在Access-Control-Max-Age头指定的时间范围内,浏览器不会再次发送OPTIONS请求,而是直接发送实际的POST请求,不管POST请求成功还是失败,在设置的时间范围内,同一个接口请求是绝对不会再次发送OPTIONS请求的。

后端需要注意的是,我这里设置允许请求的方法是config.addAllowedMethod("*"),*表示允许所有HTTP请求方法。如果未设置,则默认只允许“GET”和“HEAD”。你可以设置的HTTPMethod为GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

经过我的测试,OPTIONS无需手动设置,因为单纯只设置OPTIONS也无效。如果你设置了允许POST,代码为config.addAllowedMethod(HttpMethod.POST); 那么其实已经默认允许了OPTIONS,如果你只允许了GET,尝试发送POST请求就会报错。

举个例子,这里只允许了GET请求,当我们尝试发送一个POST非简单请求,预检请求返回403,服务器拒绝了OPTIONS类型的请求,因为你只允许了GET,未配置允许OPTIONS请求,那么浏览器将收到一个403 Forbidden响应,表示服务器拒绝了该OPTIONS请求,POST请求的状态显示CORS error

在Spring Boot中,配置允许某个请求方法(如POST、PUT或DELETE)时,OPTIONS请求通常会被自动允许。这意味着在大多数情况下,后端开发人员不需要特意考虑OPTIONS请求。这种自动允许OPTIONS请求的行为取决于使用的跨域处理库或配置,最好还是显式地允许OPTIONS请求。

点击关注,第一时间了解华为云新鲜技术~

从前后端的角度分析options预检请求的更多相关文章

  1. 对CROS OPTIONS预检请求的一些思考

    前后端分离模大势所趋,跨域问题更是老生常谈. 问题背景: 浏览器最基本的安全规范-同源策略.所谓同源是指域名.协议.端口相同.不同源的浏览器脚本(javascript.ActionScript.can ...

  2. OPTIONS预检请求

    OPTIONS预检请求 背景:像后台发送一个post请求,出错,错误代码403(权限验证未通过),查看请求方法变成OPTIONS.检查请求头得知,当前请求token未带上,问题,为什么post请求变成 ...

  3. 跨域请求中预检请求options之坑

    一.前言 因为跨域请求,浏览器可能(后面讲)会发送一次options请求,如果处理不好,跨域还是会gg的. 之前很少涉及跨域,涉及也是简单请求(下面阮老师文章中区别热简单请求和复杂请求),所以基本不会 ...

  4. CORS预检请求详谈

    引言 最近在项目中因前后端部署不同地方,前端在请求后端api时发生了跨域请求,我们采用CORS(跨域资源共享)来解决跨域请求,这需要前后端的配合来完成.在这一过程中,后端支持了CORS跨域请求后,前端 ...

  5. Cross-origin resource sharing JSON with Padding 同源策略 JSONP 为什么form表单提交没有跨域问题,但ajax提交有跨域问题? XMLHttpRequest and the Fetch API follow the same-origin policy 预检请求(preflight request)

    https://zh.wikipedia.org/wiki/跨来源资源共享 跨来源资源共享(CORS)是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略[1 ...

  6. preflight request预检请求

    preflight request预检请求,负责检查是否允许跨域请求,但是注意并不是所有的跨域请求都会发送preflight请求.对与那些幂等的请求,如GET请求,就不会发送preflight请求.只 ...

  7. ASP Net Core – CORS 预检请求

    CORS(跨源资源共享)是一种机制,它允许同一个来源运行的Web应用程序从在另一个来源运行的服务器访问资源.同源策略是一种非常严格的措施,因为它只允许与服务器起源于同一源的应用程序访问其资源.很多时候 ...

  8. 跨域时发送预检请求,tp5的restful无options方法的解决方案

    解决 跨域问题解决 问题:使用vue-resource发送delete请求时报options请求404 思考:明明发送的是delete请求,为何变成了options请求? 答:跨域情况下,PUT,DE ...

  9. 在fetch方法中添加header后遇到的预检请求问题

    今天在使用fetch方法 fetch('xxx.com',{header:{bbbbbbb:111}}) 浏览器返回的请求信息中,header变成了 :authority:koss.nocorp.me ...

  10. web api 设置允许跨域,并设置预检请求时间

    <httpProtocol> <customHeaders> <!--响应类型 (值为逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法)--> <ad ...

随机推荐

  1. luffy项目全局配置

    一:media文件夹配置 settings.dev下配置media文件夹,暴露资源 media_path=BASE_DIR.parentMEDIA_ROOT=Path(media_path).join ...

  2. 云原生API网关全生命周期管理Apache APISIX探究实操

    @ 目录 概述 定义 NGINX 与 Kong 的痛点 APISIX 的技术优势 特性 架构 应用场景 主要概念 部署 快速入门 quickstart安装 Admin API创建路由 RPM安装 安装 ...

  3. opencv-python 2 图像基本操作

    图像的基本操作 获取并修改图像的像素值 可以通过行和列的坐标值获取该像素点的像素值.对于BGR图像,它返回一个蓝色,绿色,红色值的数组.对于灰度图像,仅返回相应的强度值. 可以用同样的方法修改像素点的 ...

  4. vue对象深拷贝(避免对象赋值,改变一个对象的值,另一个对象也变化)

    对象直接赋值时,例如 let b = { name: 'name', age: 12} let a=b, a.age = 13 console.log(b.age)  // 13 由此可见,当改变 a ...

  5. ChatGPT 开源了第一款插件,都来学习一下源码吧!

    3 月 23 日,OpenAI 又投出了一枚重磅炸弹:为 ChatGPT 推出插件系统! 此举意味着 ChatGPT 将迎来"APP Store"时刻,也就是围绕它的能力,形成一个 ...

  6. 「刷起来」Go必看的进阶面试题详解

    勤学如春起之苗,不见其增日有所长:辍学如磨刀之石,不见其损日有所亏. 本文的重点:逃逸分析.延迟语句.散列表.通道.接口. 1.逃逸分析 逃逸分析是Go语言中的一项重要优化技术,可以帮助程序减少内存分 ...

  7. LAL v0.34.3发布,G711音频来了,Web UI也来了

    Go语言流媒体开源项目 LAL 今天发布了v0.34.3版本. LAL 项目地址:https://github.com/q191201771/lal 老规矩,简单介绍一下: ▦ 一. 音频G711 新 ...

  8. 【杂绪】#4 & 【Diary】CSP-S1 2021 游记(慎) & CSP-S 备赛发狂日记

    减少..减少掉 我没法同时做那么多事情了........ -------------------------------------------- 但是我\(\color{#EEEEEE}{真真切切地 ...

  9. 51nod 1153 选择子序列

    51nod 选择子序列 这道题是\(Bunny\)学长在给我们的模拟赛中的一道题. 食用单调栈,处理每个数\(a_i\)左右第一个比自己大的数的下标\(left_i\),\(right_i\),并且建 ...

  10. Ubuntu系统Flameshot使用问题

    Ubuntu系统Flameshot使用问题 系统:Ubuntu22.04 问题:使用Flameshot,每次都会先截取整个屏幕,提示需要先分享,再使用Flameshot的功能 安装Flameshot ...