# 2. CORS 進階之 Preflight 請求
#### 1. Access-Control-Allow-Origin
上一篇[從跨域到CORS(一)](http://www.rails365.net/articles/cong-kua-yu-dao-cors-yi)文章有說過CORS的基本使用,也實現了跨域的請求。本篇來講講CORS更高階的用法。
首先來講講`Access-Control-Allow-Origin`的用法。
`Access-Control-Allow-Origin:*`表示的是允許任何的來源都可以訪問,然而這并不符合大多數人的需求。假如我們只需要固定的一臺或幾臺機器,或者是某個域名下的才可以訪問,這又該如何呢?
還是按照先前的例子,從localhost:3000的服務跨域到nginx服務localhost:8080。現在來演示一下,只允許localhost:3000的訪問,在nginx是這樣配置的。
```
location / {
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000';
}
```

可見,是成功的。
也可以試一下把`http://localhost:3000`前面的`http://`去掉,會發現是不成功的,畢竟不同的協議也是不同的域的。

下面有個配置可以參考,在nginx中設置域名的訪問。
```
set $cors '';
if ($http_origin ~* 'https?://(localhost|www\.yourdomain\.com|www\.yourotherdomain\.com)') {
set $cors 'true';
}
if ($cors = 'true') {
add_header 'Access-Control-Allow-Origin' "$http_origin";
}
```
在上面的配置中,`$http_origin`表示的是獲得來源域的域名,也就是請求頭`Origin`的內容。
#### 2. Preflight請求
之前在瀏覽器模擬跨域請求的js是這樣的:
```
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://localhost:8080", true);
xhttp.send();
```
使用的方法是GET,CORS跟jsonp等方法不一樣的,它可以支持像POST,DELETE方法。
先用POST方法來試一下,nginx那邊需要改一下:
```
server {
error_page 405 =200 $uri;
}
```
再把js中的`GET`改成`POST`方法。

可見,是成功的。如果改成DELETE方法呢?

報錯了,大體上說DELETE方法是不被允許的。來看下產生的請求。

使用DELETE方法時,并不會真正的產生DELETE請求,而是先生成了一個叫OPTIONS的請求。
這個OPTIONS請求是怎么回事呢?
這個OPTIONS請求也叫Preflight請求,它是在發起DELETE真實請求時,先詢問服務器是否支持DELETE方法,再用響應頭信息的方式返回給瀏覽器,瀏覽器根據響應信息查看服務器是否支持DELETE方法,如果支持的話,就再發起真實的DELETE方法,不支持就報錯了。所以出現了上面的報錯信息。
我們先讓服務器支持DELETE方法,再來嘗試新的請求。
```
location / {
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000';
add_header 'Access-Control-Allow-Methods' 'DELETE';
}
```

果然,成功了。且發出了真正的DELETE請求。

從前面的分析可以知道,`GET`,`POST`是不會發出Preflight請求的,而DELETE方法會,那還有什么情況下會呢?下面列舉出來:
- 使用的方法不是GET, POST, 或 HEAD的任何一種
- Content-Type請求來不是下面任何一種
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 不尋常的請求頭,例如不是下面的幾種:
- Accept
- Accept-Language
- Content-Language
- XMLHttpRequest的upload事件
值得注意的是,為了避免困惑,在`Access-Control-Allow-Methods`都會顯式地寫上`GET`,`POST`方法,即使它是默認就支持的。
```
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE'
```
除此之外,頻繁的Preflight請求可能會造成性能的消耗。我們可以用下面的方法處理一下:
```
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
```
`Access-Control-Max-Age`可以設置請求的過期時間,單位是秒(second)。另外,返回204的狀態碼,是返回一個空內容的返回,畢竟,Preflight請求只需要響應的頭部信息(Access-Control-Allow-Methods),并不需要響應內容的。
下一篇: [CORS進階之設置請求頭信息(三)](http://www.rails365.net/articles/cors-jin-jie-zhi-she-zhi-qing-qiu-tou-xin-xi-san)
完結。