当前位置: 首页 > 工具软件 > PHP CORS > 使用案例 >

CORS跨域预检

田慈
2023-12-01

CORS跨域预检

简单记录一下CORS跨域预检踩得坑。

0、搜索匹配

  • 前端莫名自动发起两次请求,而代码中只请求了一次。
  • 前端自动发起OPTIONS请求。
  • 后端设置了Access-Control-Allow-Origin允许跨域,前端仍 CORS error跨域错误。

1、背景

前端页面地址:http://localhost:4001
后端接口地址:http://localhost:8080

前端跨域请求后端,方式的GET,请求头中添加了我们自定义的名为token的header,请求情况大致如下:

GET http://localhost:8080/login.action

headers:

KEYVALUE
省略默认的HTTP标准头
Token002e9b401eca4f28b4221dc141541e3f

对应的大致代码如下:

    let url = "http://localhost:8080/login.action";
    const headers = new HttpHeaders({ "Token": "002e9b401eca4f28b4221dc141541e3f" });
    this.httpClient.get<any>(url, { headers: headers }).subscribe({
      next: (v) => { console.log("成功了") },
      error: (e) => { console.log("失败了") },
      complete: () => { console.log("完成了") }
    });

2、问题

表现是请求未成功。F12开发者选项检查网络请求,发现前端发起了两次接口请求,请求方式分别是OPTIONSGET,但是前端代码中确实只写了一次请求。

其中OPTIONS请求的结果是 200GET请求的结果是 CORS error

后端Debug调试发现,第一次请求是OPTIONS,且请求头里面并没有 Token ,断点继续往下走,后端没有抛出异常,成功响应了“ 200 [OK] ”。

诡异的是,在我后端断点中断的时候,前端F12开发者工具中看到两次请求都是 pending (表示请求阻塞中,服务器还未响应请求结果),当我放开断点,两个请求同时有了结果,即一个 200[OK] ,一个 CORS error 跨域错误。且后端并未收到第二次请求(即应为GET方式的请求)。

3、分析

经查阅资料发现,这种两次请求的行为叫 【CORS跨域预检 / CORS Preflight】 ,满足某些条件的请求将会触发该行为,具体规则见尾部附加的参考资料。个人理解是,OPTIONS请求完成后浏览器会检查响应结果(尤其是response header响应头),对于不符合条件的响应结果浏览器将直接按CORS error跨域错误来处理(并不会发起正式请求),对于符合条件的响应结果才会正式发起请求(即本文中的GET请求)。

4、解决

解决方法是在响应体中添加满足CORS预检条件的response header响应头(仅列出必须携带的响应头):

Access-Control-Allow-Origin: https://your.domain.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Allow-Headers: X-Custom-Header, My-DIY-Header

讲解:

  • Access-Control-Allow-Origin头是大家最熟悉的允许跨域的站点,许多懒省事的开发者会将其不安全的设置为 Access-Control-Allow-Origin: *,即允许所有站点的跨域请求。

  • Access-Control-Allow-Methods是大多数查询CORS跨域预检资料时所被提到的,本文中我想要正式请求的方式是GET,因此该响应头的值中至少应包含GET,即 Access-Control-Allow-Methods: GET。该值可以为多种请求方式,中间用英文逗号分隔。

  • Access-Control-Allow-Headers便是我被坑到的地方,大多数资料都会提到上面的请求方法,但很少有提到这个响应头。由于我添加了叫 Token的自定义请求头,但我的后端并没有在响应头中添加 Access-Control-Allow-Headers: Token,所以导致前后端一直跨域错误,加上这个就解决了。这个响应头的值和上面那个一样,可以为多个值,中间英文逗号分隔。

5、参考

  1. Preflight request - MDN Web Docs Glossary: Definitions of Web-related terms | MDN

  2. Access-Control-Allow-Headers - HTTP | MDN

Source: https://docs.ximinghui.org/751083ef19c8.html

 类似资料: