CORS：クロスドメインなAjaxでJSON / JSONPの各種ブラウザ対応まとめ


忘れてしまうのでまとめておく。

# 気をつけること

- 基本的に jsonp であれば動く
- json + モダンブラウザは、Access-Control-Allow-Originを設定すれば動く
- json + IE8,9では、XDomainRequestを使えば動く
- IE6,7は死亡（もういいよね）
- プロトコル(http|https) / FQDN / ポート番号、いずれかが違う場合、クロスドメイン
- BASIC認証があると、別途対応が必要（後述）

# JSON編

## サーバサイド

jsonを返すサーバ側にAccess-Control-Allow-Originを設定する。

```php
<?php
	header('Access-Control-Allow-Origin: *');
	header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');

	// CakePHPの場合
	$this->header('Access-Control-Allow-Origin: *');
	$this->header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
```

### BASIC認証をしている場合

クレデンシャルを許可するといけるが、Originにワイルドカードは指定できない。

```php
<?php
	header('Access-Control-Allow-Origin: http://example.com');
	header('Access-Control-Allow-Credentials: true');
	header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization');
```


## クライアントサイド

### jQuery版

```ajax.js
// IE8,9でXMLHttpRequestの代わりにXDomainRequestを使う
(function( jQuery ) {

if ( window.XDomainRequest ) {
	jQuery.ajaxTransport(function( s ) {
		if ( s.crossDomain && s.async ) {
			if ( s.timeout ) {
				s.xdrTimeout = s.timeout;
				delete s.timeout;
			}
			var xdr;
			return {
				send: function( _, complete ) {
					function callback( status, statusText, responses, responseHeaders ) {
						xdr.onload = xdr.onerror = xdr.ontimeout = xdr.onprogress = jQuery.noop;
						xdr = undefined;
						complete( status, statusText, responses, responseHeaders );
					}
					xdr = new XDomainRequest();
					xdr.open( s.type, s.url );
					xdr.onload = function() {
						callback( 200, "OK", { text: xdr.responseText }, "Content-Type: " + xdr.contentType );
					};
					xdr.onerror = function() {
						callback( 404, "Not Found" );
					};
					xdr.onprogress = function() {};
					if ( s.xdrTimeout ) {
						xdr.ontimeout = function() {
							callback( 0, "timeout" );
						};
						xdr.timeout = s.xdrTimeout;
					}
					xdr.send( ( s.hasContent && s.data ) || null );
				},
				abort: function() {
					if ( xdr ) {
						xdr.onerror = jQuery.noop();
						xdr.abort();
					}
				}
			};
		}
	});
}
})( jQuery );

jQuery.getJSON('http://example.com/search.json',
	{
		'hoge': 'piyo'
	},
	function (data) {
		console.log(data);
	}
});
```

参照 : https://github.com/tlianza/ajaxHooks/blob/master/src/ajax/xdr.js

### JavaScript版

```ajax.js
function createCORSRequest(method, url) {
	var xhr = new XMLHttpRequest();
	if ("withCredentials" in xhr) {
		xhr.open(method, url, true);
	} else if (typeof XDomainRequest != "undefined") {
		xhr = new XDomainRequest();
		xhr.open(method, url);
	} else {
		xhr = null;
	}
	return xhr;
}

var xhr = createCORSRequest('GET', 'http://example.com/search.json');
if (!xhr) {
	throw new Error('CORS not supported');
}
xhr.onload = function(){};
xhr.onerror = function(){};
xhr.send();
```

参照 : http://www.html5rocks.com/en/tutorials/cors/

# JSONP編

jsonpの場合、サーバサイドではjsonp形式で返すだけで特に設定は必要ない。ただし、jsonと違いJavaScriptのコードをクライアントサイドで直接実行するため、jsonpの提供APIに悪意のあるコードが含まれていた場合は恐ろしいことになる。callbackで指定できるパラメータを制限するなど、ある程度の工夫が必要。

## クライアントサイド

### jQuery版

```ajax.js
jQuery.getJSON('http://example.com/search.json?callback=?',
	{
		'hoge': 'piyo'
	},
	function (data) {
		console.log(data);
	}
});
```

calback=?とすることで、jQueryが勝手にコールバック名を生成してアクセスしてくれる。

### JavaScript版

```ajax.js
var target = document.createElement('script');
target.charset = 'utf-8';
target.src = 'http://example.com/search.json?callback=hoge';
document.body.appendChild(target);

function hoge(result) {
	console.log(result);
}
```