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

options预检恳求是干嘛的?options恳求必定会在post恳求之前发送吗?前端或者后端开发需求手动干预这个预检恳求吗?不用文档界说堆砌名词,从前后端视点独自剖析,大白话带你了解!

从前端的视点看options——post恳求之前必定会有options恳求?信口雌黄!

你是否常常看到这种跨域恳求过错?

从前后端的角度分析options预检请求

这是由于服务器不答应跨域恳求,这里会深入讲一讲OPTIONS恳求。

只要在满意必定条件的跨域恳求中,浏览器才会发送OPTIONS恳求(预检恳求)。这些恳求被称为“非简略恳求”。反之,假如一个跨域恳求被以为是“简略恳求”,那么浏览器将不会发送OPTIONS恳求。

简略恳求需求满意以下条件:

  1. 只运用以下HTTP办法之一:GETHEADPOST
  2. 只运用以下HTTP头部:AcceptAccept-LanguageContent-LanguageContent-Type
  3. Content-Type的值仅限于:application/x-www-form-urlencodedmultipart/form-datatext/plain

假如一个跨域恳求不满意以上一切条件,那么它被以为对错简略恳求。关于非简略恳求,浏览器会在实践恳求(例如PUTDELETEPATCH或具有自界说头部和其他Content-TypePOST恳求)之前发送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-urlencodedmultipart/form-datatext/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预检请求

2.从后端的视点看options——post恳求之前必定会有options恳求?胡说八道!

在装备跨域时,服务器需求处理OPTIONS恳求,以便在呼应头中回来跨域装备信息。这个过程一般是由服务器的跨域中间件(Node.jsExpress结构的cors中间件、PythonFlask结构的flask_cors扩展)或过滤器(JavaSpringBoot结构的跨域过滤器)自动完结的,而无需开发人员手动处理。

以下是运用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”。你能够设置的HTTPMethodGET, 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

从前后端的角度分析options预检请求
从前后端的角度分析options预检请求

Spring Boot中,装备答应某个恳求办法(如POSTPUTDELETE)时,OPTIONS恳求一般会被自动答应。这意味着在大多数情况下,后端开发人员不需求特意考虑OPTIONS恳求。这种自动答应OPTIONS恳求的行为取决于运用的跨域处理库或装备,最好仍是显式地答应OPTIONS恳求。

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