本次遇到 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
它是一種探測性的請求,經過這個方法,客戶端能夠在採起具體資源請求以前,決定對該資源採起何種必要措施,或者瞭解服務器的性能。chrome
在 ajax 中出現 options 請求,也是一種提早探測的狀況,ajax 跨域請求時,若是請求的是 json,就屬於複雜請求,所以須要提早發出一次 options 請求,用以檢查請求是不是可靠安全的,若是 options 得到的迴應是拒絕性質的,好比 404\403\500 等 http 狀態,就會中止 post、put 等請求的發出。json
是瀏覽器對簡單跨域請求和複雜跨域請求的處理區別後端
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 呢?
前端繞不過去
- 要麼後端代碼 cors 處理,
- 要麼 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;
}
}
複製代碼
兩者適合的業務場景不一樣