阅读 488

HTTP之options预检请求

前置知识点

HTTP请求方法

简单请求

预检请求

  • 只有跨域的情况下,才会发生预请求
  • 与前述简单请求不同,“需预检的请求”要求必须首先使用OPTIONS方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。“预检请求”的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

withCredentials为true不会产生预请求

哪些情况下会产生预请求?

1. 请求头Content-Type为application/json会产生预请求

前端代码:

function run(){
            let xhr = new XMLHttpRequest()
            xhr.open('POST','http://127.0.0.1:8080/Public//testPost')
            xhr.setRequestHeader('Content-Type','application/json')
            xhr.onload = function(res){
                console.log(res)
            }
            xhr.send()
        }

        run()
复制代码

后端代码(php版本):

 public function testPost(){
        header("Access-Control-Allow-Headers: Content-Type");
        header("Access-Control-Allow-Origin: *");
        echoJsonResult(1,'',$_POST);
    }
复制代码

当后端没有配置Access-Control-Allow-Headers包含Content-Type,报错如下:

Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
复制代码

当后端没有配置Access-Control-Allow-Origin,报错如下

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
复制代码

浏览器发出的两次请求:

  • OPTIONS请求

image.png

image.png

image.png

  • POST请求

image.png

image.png

image.png

2. 设置了用户自定义请求头会产生预检请求

前端代码:

        let xhr = new XMLHttpRequest()
            xhr.open('POST','http://127.0.0.1:8080/Public//testPost')
            xhr.setRequestHeader('abc','123')
            xhr.onload = function(res){
                console.log(res)
        }
         xhr.send()
复制代码

后端代码:

     public function testPost(){
        header("Access-Control-Allow-Origin: *");
        header('Access-Control-Allow-Headers: abc');
        echoJsonResult(1,'',$_POST);
    }
复制代码

OPTIONS请求的request请求头

Access-Control-Request-Headers: abc
Access-Control-Request-Method: POST
复制代码

OPTIONS请求的response响应头

Access-Control-Allow-Headers: abc
Access-Control-Allow-Origin: *
复制代码

当后端没有配置Access-Control-Allow-Headers为abc时 ,报错如下

Request header field abc is not allowed by Access-Control-Allow-Headers in preflight response.
复制代码

3. delete方法产生预检请求

前端代码:

            let xhr = new XMLHttpRequest()
            xhr.open('delete','http://127.0.0.1:4002/del')
            xhr.onload = function(res){
                console.log(res)
            }
            xhr.send()
复制代码

后端代码:

    res.setHeader('Access-Control-Allow-Origin','*')
    res.setHeader('Access-Control-Allow-Methods','DELETE')
    res.end('ok')
复制代码

浏览器发出的两次请求:

image.png

image.png

当后端没有配置Access-Control-Allow-Methods时,报错如下

Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.
复制代码

预检请求不一定每一次都会产生

  • 这个因为浏览器会对预检请求进行缓存
  • 同时通过服务器端设置 Access-Control-Max-Age 字段来设置缓存时间
  • 那么当第一次请求该 URL 时会发出 OPTIONS 请求,浏览器会根据返回的 Access-Control-Max-Age 字段缓存该请求的 OPTIONS 预检请求的响应结果(具体缓存时间还取决于浏览器的支持的默认最大值,取两者最小值,一般为 10 分钟)。在缓存有效期内,该资源的请求(URL 和 header 字段都相同的情况下)不会再触发预检。(chrome 打开控制台可以看到,当服务器响应 Access-Control-Max-Age 时只有第一次请求会有预检,后面不会了。注意要开启缓存,去掉 disable cache 勾选。)
  • 在 Firefox 中,上限是24小时 (即 86400 秒)。
  • 在 Chromium v76 之前, 上限是 10 分钟(即 600 秒)。
  • 从 Chromium v76 开始,上限是 2 小时(即 7200 秒)。
  • Chromium 同时规定了一个默认值 5 秒。
  • 如果值为 -1,表示禁用缓存,则每次请求前都需要使用 OPTIONS 预检请求。
 res.setHeader('Access-Control-Max-Age',600) //10秒
复制代码

CORS跨域

当请求跨域,后端没有配置时,报错如下

Access to XMLHttpRequest at 'http://127.0.0.1:8080/Public//testPost' from origin 'http://10.200.137.43:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
复制代码

则后端配置代码:

     public function testPost(){
        header("Access-Control-Allow-Origin: *");
        echoJsonResult(1,'',$_POST);
    }
复制代码

withCredentials为true时需要后端配置响应头Access-Control-Allow-Credentials为true (不会产生预请求)

前端代码:

         let xhr = new XMLHttpRequest()
            xhr.open('POST','http://127.0.0.1:8080/Public//testPost')
            xhr.withCredentials = true
            xhr.onload = function(res){
                console.log(res)
          }
          xhr.send()
复制代码

后端代码:

    public function testPost(){
        header("Access-Control-Allow-Origin: http://10.200.137.43:8080");
        header("Access-Control-Allow-Credentials: true");
        echoJsonResult(1,'',$_POST);
    }
复制代码

浏览器只发起一次POST请求:

image.png

image.png

image.png 当后端配置Access-Control-Allow-Origin为星号时,报错如下:

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
复制代码

当后端没有配置Access-Control-Allow-Credential时,报错如下

The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
复制代码

参考链接

文章分类
前端
文章标签