CORSリクエストについてのまとめ
http://www.html5rocks.com/en/tutorials/cors/#disqus_thread
Author
Monsur Hossain
ほとんどこちらの要約
導入
aaaa.comにはbbbb.comがアクセスしたいデータがあるとしよう。このクロスドメインリクエストは従来same-origin -policy によって禁止されている。解決法としては次の3つ。
- JSONP
- カスタムプロキシ
- Cross-Origin Resource Sharing (CORS)
JSONPはセキュリティ面に問題があり、カスタムプロキシはセットアップと保守が面倒。ここでは、W3Cの仕様であるCORSを選択する。
CROSリクエストを行うことで、レスポンスに特別なヘッダが付与され、クロスドメインのデータにアクセスすることができるようになる。
CORSリクエストの作成
以下JavaScriptでクロスドメインリクエストを作成する。
対応しているブラウザはこちら Chrome, Firefox, Opera, SafariはすべてXMLHttpRequest2 objectを使う。
イベントハンドラ
XMLHttpRequestには幾つかイベントハンドラが用意されているが、ほとんどの場合onload
とonerror
イベントが最低限必要となる。
xhr.onload = function() { var responseText = xhr.responseText; console.log(responseText); // レスポンスの処理 }; xhr.onerror = function() { console.log('There was an error!'); };
withCredential
リクエストにクッキーを付与するため.withCredentials
プロパティをtrueにする。
xhr.withCredentials = true:
これが機能するために、サーバーもAccess-Control-Allow-Credentialsレスポンスヘッダを"true"にして、credentialsを可能にする必要がある。
Access-Control-Allow-Credentials: true
リクエストの作成
send()
メソッドを呼び出す。リクエストがbodyを持っているなら、引数に取ることができる。
xhr.send();
シンプルリクエスト
var url = 'http://api.aaaa.com/cors'; var xhr = createCORSRequest('GET', url); xhr.send();
HTTP Request:
GET /cors HTTP/1.1
Origin: http://api.bbbb.com
Host: api.aaaa.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
有効なCORSリクエストにはOrigin headerが含まれる。ブラウザによって追加され、ユーザーは操作することはできない。
HTTP Response:
Access-Control-Allow-Origin: http://api.bbbb.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin
(必須) - オリジンリクエストヘッダか、*をとりあらゆるサイトからのアクセスを許可する。
Access-Control-Allow-Credentials
(オプション) - クッキーを扱えるようにする。これをtrue
にして機能させるには、同時にXMLHttpRequestオブジェクトのwithCredentialsプロパティをtrue
にしなければならない。
Access-Control-Expose-Headers
(オプション) - XMLHttpRequest 2 オブジェクトは以下のレスポンスヘッダの値を返すgetResponseHeaders()
メソッドを持つ。
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
その他のヘッダにクライアントからのアクセスを許可するには、Access-Control-Expose-Headers
を用いる。
PUT, DELETE, JSON content
ブラウザはまずpreflightリクエストを行い、サーバーに対し実際のリクエストの許可をもらう。許可が下りて初めて、ブラウザは実際のリクエストを行う。
var url = 'http://api.aaaa.com/cors'; var xhr = createCORSRequest('PUT', url); xhr.setRequestHeader( 'X-Custom-Header', 'value'); xhr.send();
Preflight Request:
OPTIONS /cors HTTP/1.1
Origin: http://api.bbbb.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.aaaa.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Access-Control-Request-Method
- 実際のリクエストのHTTPメソッド。
Access-Control-Request-Headers
- コンマで区切られた、リクエストに含まれるヘッダのリスト。
HTTPメソッドとヘッダが有効なら、サーバーは次のようなレスポンスを返す。
Preflight Response:
Access-Control-Allow-Origin: http://api.bbbb.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin
(必須) - シンプルレスポンスと同じく、preflightレスポンスもこのヘッダを含む。
Access-Control-Allow-Methods
(必須) - サポートされるすべてのHTTPメソッド。
Access-Control-Allow-Headers
(リクエストにAccess-Control-Request-Headers header
が含まれる場合、必須) - サポートされるすべてのリクエストヘッダ。
Access-Control-Allow-Credentials
(オプション) - シンプルリクエストと同じ。
Access-Control-Max-Age
(オプション) - リクエストの度にpreflightリクエストを行うと重くなる。preflightレスポンスは、このヘッダに指定された秒数キャッシュされる。
Actual Request:
PUT /cors HTTP/1.1
Origin: http://api.bbbb.com
Host: api.aaaa.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Actual Response:
Access-Control-Allow-Origin: http://api.bbbb.com
Content-Type: text/html; charset=utf-8
サーバーがCORSリクエストを拒否する場合、CORSヘッダを含まないレスポンスが返ってくる。レスポンスにCORSヘッダがないために、ブラウザはリクエストは無効と判断し、実際のリクエストを行わない。
Preflight Request:
OPTIONS /cors HTTP/1.1
Origin: http://api.bbbb.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.aaaa.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Preflight Response:
// ERROR - 無効なリクエスト
Content-Type: text/html; charset=utf-8
jQuery
$.ajaxメソッドを用いると、XHRとCORSリクエストの両方を使用することができる。
$.ajax({ // HTTP メソッド. // 'PUT'あるいは'DELETE'で、preflightリクエストをトリガー. type: 'GET', // リクエストの宛先URL url: 'http://api/aaaabbbb.com', // デフォルトの'application/x-www-form-urlencoded; charset=UTF-8' // では、preflightをトリガーしない。 // application/x-www-form-urlencoded, multipart/form-data, text/plain // 以外のタイプを設定すると、preflightをトリガーする。 xhrFields: { // XMLHttpリクエストについての追加の情報 withCredentials: false }, headers: { }, success: function() { // 成功時のレスポンスの扱いを記述 }, error: function() { // 失敗時のレスポンスエラーの扱いを記述 } });