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

本文分享自华为云社区《从前后端的角度分析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请求。请求满足以下简单请求条件:
使用
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请求。请求不满足简单请求条件,因为:
使用了非允许范围内的
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的一个跨域过滤器,供参考
这里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请求。
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/f0f0e9bb81a650d318e546684】。文章转载请联系作者。










评论