从前后端的角度分析options预检请求——打破前后端联调的理解障碍

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

1.从前端的角度看options——post请求之前一定会有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请求的返回
在这里插入图片描述


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

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



欢迎一键三连~



有问题请留言,大家一起探讨学习


----------------------Talk is cheap, show me the code-----------------------

目录
相关文章
|
4月前
|
前端开发 NoSQL 测试技术
Crossbar 后端开发调试混乱解决方案
Crossbar 后端开发调试混乱解决方案
|
7月前
|
安全 数据中心
适合自身业务的HTTP代理有哪些因素决定?
适合自身业务的HTTP代理有哪些因素决定?
|
10月前
|
存储 前端开发
网络基础 登录对接CAS-跨域导致的一个意想不到的Bug
网络基础 登录对接CAS-跨域导致的一个意想不到的Bug
147 1
|
10月前
|
缓存 前端开发
前端如何在没有后端配合的情况下获取服务器时间?
现在有一个需求,前端需要同步服务器的时间,因为前端的时间是不可靠的,解决方案有很多,但是不管什么方案,都是需要和服务端进行交互,那么怎么减轻后端的工作量直接实现呢?
293 0
前端如何在没有后端配合的情况下获取服务器时间?
Odoo 增加web后端的响应能力
Odoo 增加web后端的响应能力
89 0
|
前端开发
前端工作总结226-后端数据接口问题类型错误
前端工作总结226-后端数据接口问题类型错误
53 0
前端工作总结226-后端数据接口问题类型错误
|
前端开发
前端工作小结11-封装请求
前端工作小结11-封装请求
80 0
|
前端开发 IDE Java
前后端,相煎何太急...
前沿:哈喽,我是树酱。今天分享的水文是关于前端与后端之间battle的那些事,自从前后端分离之后,这场分家带来了不少沟通问题,甚至产生battle。我们先回顾下前后端battle的前世今生
191 0
前后端,相煎何太急...
|
人工智能 安全 JavaScript
前后端通讯:非敏感信息Cookie的"强化"之路
我们公司鉴权走的是JWT, 但是有些数据走Cookie更方便通讯, 纵观今天,网上一大把说Cookie不好的文章. 但是我们还是要用,那怎么安全一丢丢呢?
121 0
|
Web App开发 前端开发 安全
不同角度看跨域那些事
前沿:跨域是一个老生常谈的话题,树酱在github上无意间看到cors-anywhere这个开源库,加上最近身边有一些实习童鞋在本地开发环境中提出疑惑🤔,本地开发是如何解决跨域问题的?结合以上,也就有了今天的主题,前端跨域解决方案那些事。跨域解决方案比较多,🌲酱君把平时接触比较多的share一下
128 0
不同角度看跨域那些事

热门文章

最新文章