Fork me on GitHub

关于post请求产生的preflight request小记

关于post请求产生的preflight request小记

首先感谢几篇文章的大佬,学习到很多

 

  • 记一次跨域post请求数据之preflight request
  • 跨域请求出现preflight request失败的问题的解决
  • 为什么axios先请求options在请求post及解决方法

以上文中,大佬们已经总结了很多也很详细,以下是个人的一些小结

 

一、问题背景

本地启动的前端vue项目使用axios发送post请求去获取一个展示列表,对于可能会产生的跨域请求后端项目中已经配置了如下

 

1
2
3
4
response.setHeader("Access-Control-Allow-Origin",request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token");

前端请求为

 

1
2
3
4
5
6
7
axios.post(myUrl, {
    token: myToken,
}).then( res => {
    // do sth success
}).catch( err => {
    // do sth error
})

 

 

然后浏览器控制台显示为

 

 

 

 

二、初步分析

首先看到报错中最熟悉的关键字CORS,初步判定是跨域问题,但后端已经配置了处理跨域,所以需要把跨域问题排除掉,接着看报错的文字Redirect is not allowed for a preflight request.,其中有个关键字应该是preflight request,普通的跨域报错好像没有这个,接下来就可以去看文章开头大佬们的解析了,分析前首先也要明确几点:

 

  • axios使用post请求时,会默认先发送一个option请求
  • axios使用post请求时,默认的Content-Type是application/json
  • 使用FormData格式的参数作为post请求的参数时,不会出现跨域问题和preflight request报错

2.1 请求类别

  • 简单请求
  • 非简单请求

2.1.1简单请求

满足以下两个条件

 

  1. 请求方法是以下三种方法之一:
  • HEAD
  • GET
  • POST
  1. Content-Type: (仅当POST方法的Content-Type值等于下列之一才算做简单需求)
  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

 

 

对于简单请求,浏览器直接发出CORS请求。

 

  • Access-Control-Allow-Origin: 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
  • Access-Control-Allow-Credentials: 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
  • Access-Control-Expose-Headers: 该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

2.1.2非简单请求

  • 请求方法不是GET/HEAD/POST
  • POST请求的Content-Type并非application/x-www-form-urlencoded, multipart/form-data, 或text/plain
  • 请求设置了自定义的header字段
  • 非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json
  • 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)
  • "预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源
  • 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错
  • 除了Origin字段,"预检"请求的头信息包括两个特殊字段:
    • Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法
    • Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段

2.1.3为什么要发预检请求

preflight request是为确保服务器是否允许发起对服务器数据产生副作用的HTTP请求方法,而预先由浏览器发起OPTIONS方法的一个预检请求,如果允许就发送真实的请求,如果不允许则直接拒绝发起真实请求。

 

三、问题回溯

  1. 使用axios的post请求,且Content-Type默认是application/json格式
  2. 由于是非简单请求,所以浏览器会先发一个option请求,虽然后端已设置解决跨域,但出现preflight request报错

四、尝试解决

4.1 方法一

在发送axios发送post请求时,配置header属性

 

1
2
3
4
5
6
7
8
9
10
11
axios({
    method: 'post',
    url: myUrl,
    params: {
        token: myToken,
    },
    header: {
        'Content-Type': 'application/x-www-form-urlencoded',
    }
})
// 我自己本地测试,改成这样后,已经可以正常获取接口数据了

4.2 方法二

使用FormData参数格式 + axios的data字段

 

1
2
3
4
5
6
7
let formData = new FormData();
formData.append('token', myToken);
axios({
    method: 'post',
    url: myUrl,
    data: formData,
})

使用formData发送请求时,axios不会预先发送option请求,直接只有一个post请求

 

4.3 方法三

使用axios的params字段

 

1
2
3
4
5
6
7
axios({
    method: 'post',
    url: myUrl,
    params: {
        token: myToken,
    },
})

 

 

使用params字段发送请求时,axios不会预先发送option请求,直接只有一个post请求

 

 

 

补充:如果axios用post请求且直接使用data字段,浏览器会报preflight request错误,而且浏览器会先发送一个option请求

posted @ 2022-09-28 17:53  思考的大腿  阅读(333)  评论(0编辑  收藏  举报