記一次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 參數,因此反向代理也能解決這個問題。
  • 是在被訪問的服務器上配置(容許訪問其餘的服務器)。

參考資料

相關文章
相關標籤/搜索