场景描述解释:

  • 有一个上传请求,使用axios直接发送一个post上传请求
  • 若是搭配了axios的第三个参数config的onUploadProgress后
  • 就会多发一个OPTIONS请求
  • 此多出来的请求,用于检查请求的方法、请求头信息等是否被目标服务器接受
  • 如下图示,一个上传,两次请求
可以理解为,浏览器会发个预检查(preflight)请求到服务器进行验证,比如验证跨域。

若服务端不处理,则会报错

  • 笔者的后端是express框架
  • 不做处理时,是这样的报错
  • 报错截图如下:

Network

Console

代码如下

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>002</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
        #progress-bar {
            width: 300px;
            height: 20px;
            border: 1px solid #ccc;
            background-color: #f1f1f1;
            margin-bottom: 10px;
        }

        #progress {
            width: 0;
            height: 100%;
            background-color: #4caf50;
        }
    </style>
</head>

<body>
    <input type="file" />
    <div id="progress-bar">
        <div id="progress"></div>
    </div>
    <script>
        let ipt = document.querySelector('input')
        ipt.addEventListener('change', async (e) => {
            let file = e.target.files[0]
            const formData = new FormData()
            formData.append('find_a_file_size_bigger_2m', file)
            await uploadFn(formData)
            e.target.value = null
        })

        let config = {
            onUploadProgress: function (progressEvent) {
                var progress = document.getElementById('progress');
                var percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
                progress.style.width = percent + '%';
                console.log('percent', percent);
            }
        };

        const uploadFn = (params) => {
            return new Promise((resolve, reject) => {
                axios.post('http://ashuai.work/api/simulateUpload', params, config) // 发两次请求,还有一个是options
                // axios.post('http://ashuai.work/api/simulateUpload', params) // 只发送一个请求
                    .then((res) => {
                        resolve(res.data)
                    })
                    .catch((err) => {
                        reject(err)
                        console.log(err);
                    });
            })
        }
    </script>
</body>

</html>

注意,因为是使用VsCode中使用Live Server启动这个html

所以ip端口默认是http://127.0.0.1:5500/xxx.html

这种情况下,就算直接配置相应头,也没用了

就真的得使用Cors控制了

比如,如下的后端代码:

// 模拟文件上传接口
route.post('/simulateUpload', function (req, res) {
  res.header('Access-Control-Allow-Origin', '*');  // vscode本地是没用的
  let apiRes = {
    code: 0,
    done: "yes",
    data: "上传数据成功"
  }
  res.send(apiRes)
});

解决方案:使用cors配置下

npm i cors

const cors = require('cors');
......

app.use(cors()); // express实例服务使用cors
  • 这样所有的都允许跨域了,都放行
  • 当然也可以指定单独的本机的

    app.use(cors({
      origin: 'http://127.0.0.1:5500',
      methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 注意,这里的OPTIONS请求方法
      allowedHeaders: ['Content-Type', 'Authorization'], // 允许自定义请求头
    }));

问:为什么会出现OPTION请求,作用是啥?

答:OPTION请求是浏览器自带的专门用于查询问一问咨询的

浏览器的OPTION说:能发请求呗?

服务器说,能,才会让后续的请求正常发起

问:大概什么情况下,会出现OPTION请求

答:

  • 跨域请求
  • 代码中加了自定义请求头
  • POST请求的Content-Type值不属于application/x-www-form-urlencoded, multipart/form-data, 或 text/plain中的任何一种

  • 正常来说,后端代码框架都会提前处理好这个问题,无论是nodejs还是java亦或是C#等服务端语言
  • 要是没有沟通加上即可

水冗水孚
1.1k 声望588 粉丝

每一个不曾起舞的日子,都是对生命的辜负