记一次GET请求以前出现OPTIONS请求

本次遇到 get 请求以前出现 options 请求,致使请求失败前端

代码示例

var ajax = new XMLHttpRequest()

ajax.open('get', '//api.abc.cn/card/getCode')
ajax.setRequestHeader('Content-Type', 'application/json;')
ajax.send()
ajax.onreadystatechange = function() {
  if (ajax.readyState == 4 && ajax.status == 200) {
    console.log(ajax.responseText)
  }
}
复制代码

chrome 会出现 options 和 get 两次请求nginx

概念阐述

跨域资源共享(Cross-Origin Resource Sharing, CORS)是为解决 Ajax 技术难实现跨域问题而提出的一个规范,这个规范试着从根本上解决安全的跨域资源共享问题。在此以前,解决此类问题的途径每每是服务器代理、JSONP 等,治标不治本。目前基本全部浏览器都已经支持该规范。ajax

  • 什么是 options 请求?

它是一种探测性的请求,经过这个方法,客户端能够在采起具体资源请求以前,决定对该资源采起何种必要措施,或者了解服务器的性能。chrome

在 ajax 中出现 options 请求,也是一种提早探测的状况,ajax 跨域请求时,若是请求的是 json,就属于复杂请求,所以须要提早发出一次 options 请求,用以检查请求是不是可靠安全的,若是 options 得到的回应是拒绝性质的,好比 404\403\500 等 http 状态,就会中止 post、put 等请求的发出。json

  • 致使出现 options 请求缘由

    是浏览器对简单跨域请求和复杂跨域请求的处理区别后端

XMLHttpRequest 会遵照同源策略(same-origin policy). 也即脚本只能访问相同协议/相同主机名/相同端口的资源, 若是要突破这个限制, 那就是所谓的跨域, 此时须要遵照 CORS(Cross-Origin Resource Sharing)机制。api

那么, 容许跨域, 不就是服务端设置 Access-Control-Allow-Origin: *就能够了吗? 普通的请求才是这样子的, 除此以外, 还一种叫请求叫 preflighted request。跨域

注意,为了安全,标准里不容许 Access-Control-Allow-Origin: *,必须指定明确的、与请求网页一致的域名浏览器

preflighted request 在发送真正的请求前, 会先发送一个方法为 OPTIONS 的预请求(preflight request), 用于试探服务端是否能接受真正的请求,若是 options 得到的回应是拒绝性质的,好比 404\403\500 等 http 状态,就会中止 post、put 等请求的发出。缓存

  • 那么, 什么状况下请求会变成 preflighted request 呢?

    1. 请求方法不是 GET/HEAD/POST
    2. POST 请求的 Content-Type 并不是 application/x-www-form-urlencoded, multipart/form-data, 或 text/plain
    3. 请求设置了自定义的 header 字段

解决方案

前端绕不过去

  1. 要么后端代码 cors 处理,
  2. 要么 nginx 配置

nginx 跨域配置

server {
  listen       80;
  server_name  api.abc.com;

  # 是否容许请求带有验证信息
  add_header Access-Control-Allow-Credentials true;
  # 容许跨域访问的域名,能够是一个域的列表,空格隔开,也能够是通配符*(不建议)
  add_header Access-Control-Allow-Origin  http://card.abc.com;
  # 容许使用的请求方法,以逗号隔开,能够用 *
  add_header Access-Control-Allow-Methods 'POST,GET,OPTIONS,PUT,DELETE';
  # 预检命令的缓存,若是不缓存每次会发送两次请求,单位为秒。
  # 第一次是浏览器使用OPTIONS方法发起一个预检请求,第二次才是真正的异步请求
  add_header Access-Control-Max-Age 3600;

  # 容许脚本访问的返回头
  add_header Access-Control-Allow-Headers 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
  # 容许自定义的头部,以逗号隔开,大小写不敏感
  add_header Access-Control-Expose-Headers 'WWW-Authenticate,Server-Authorization';

  # OPTIONS类的请求,是跨域先验请求
  if ($request_method = 'OPTIONS') {
     return 204; # http状态码 204 (无内容) 服务器成功处理了请求,但没有返回任何内容。能够返回 200
  }
  location / {
    # proxy_pass http://127.0.0.1:3000;
  }
  location /card {
    proxy_pass http://127.0.0.1:3001;
  }
  location /music {
    proxy_pass http://127.0.0.1:3002;
  }
}
复制代码

CORS 对比反向代理

两者适合的业务场景不一样

反向代理

  • 将一台服务器做为网关和代理服务器,负责将请求转发到子系统(其它服务器)
  • 从用户的角度看,我只访问了一个域名,但其实可能你访问了不少服务器
  • 是在访问其它服务器的服务器上配置(要转发到的服务器)

跨域资源共享

  • 从 ajax 里拿其它服务器的资源。
  • 能够看到,这个主要是为了解决前端人员的问题。但 ajax 里其实就有一个 url 参数,因此反向代理也能解决这个问题。
  • 是在被访问的服务器上配置(容许访问其余的服务器)。

参考资料

相关文章
相关标签/搜索