本文分享自华为云社区《从前后端的视点剖析options预检恳求——打破前后端联调的理解障碍》,作者: 砖业洋__ 。
options预检恳求是干嘛的?options恳求必定会在post恳求之前发送吗?前端或者后端开发需求手动干预这个预检恳求吗?不用文档界说堆砌名词,从前后端视点独自剖析,大白话带你了解!
从前端的视点看options——post恳求之前必定会有options恳求?信口雌黄!
你是否常常看到这种跨域恳求过错?
这是由于服务器不答应跨域恳求,这里会深入讲一讲OPTIONS
恳求。
只要在满意必定条件的跨域恳求中,浏览器才会发送OPTIONS
恳求(预检恳求)。这些恳求被称为“非简略恳求”。反之,假如一个跨域恳求被以为是“简略恳求”,那么浏览器将不会发送OPTIONS
恳求。
简略恳求需求满意以下条件:
- 只运用以下
HTTP
办法之一:GET
、HEAD
或POST
。 - 只运用以下
HTTP
头部:Accept
、Accept-Language
、Content-Language
、Content-Type
。 -
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
恳求。恳求满意以下简略恳求条件:
- 运用
POST
办法。 - 运用的
HTTP
头部仅包含Content-Type
。 -
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
恳求。恳求不满意简略恳求条件,由于:
- 运用了非答应范围内的
Content-Type
值(”application/json
” 不归于application/x-www-form-urlencoded
、multipart/form-data
或text/plain
)。 - 运用了一个自界说
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
恳求的回来
2.从后端的视点看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
恳求。
点击关注,第一时刻了解华为云新鲜技能~