Access-Control-Allow-Origin と Access-Control-Allow-Headers について

Slim3JSON を受け取るために、久しぶりに Ajax のデータを受け取る処理を記述していたら、(Chrome 上で)下記のエラーが発生した。

XMLHttpRequest cannot load http://localhost:8888/api/json/... Origin http://localhost is not allowed by Access-Control-Allow-Origin.

Access-Control-Allow-Origin

調べてみたところ、サーバーからのレスポンスに、

Access-Control-Allow-Origin: *

の HTTP ヘッダを追加する必要があるようだ。仮に Slim3 で記述をするとしたら、Controller にこう書く。

public class LatestController extends Controller {

    private PostService service = new PostService();

    @Override
    public Navigation run() throws Exception {
        String json = service.getLatestPostIn10AsJson();
        requestScope("json", json);
        response.setHeader("Access-Control-Allow-Origin", "*");
        return forward("latest.jsp");
    }

この「Access-Control-Allow-Origin」の値には、特定のドメイン名を記述して利用制限をしてもよい。*1

Access-Control-Allow-Headers

上記の設定だけで、jQuery は動いた。今度は ExtJSAjax を使ってみようとしたら、今度は、

XMLHttpRequest cannot load http://localhost:8888/api/json/.... Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers.

調べてみたら、ExtJSAjax のリクエストをする際に、ヘッダに

X-Requested-With: XMLHttpRequest

を送っていることが原因らしい。このリクエストヘッダが送信された場合には、レスポンスヘッダ

Access-Control-Allow-Headers: X-Requested-With

を返す必要がある。*2
そのため、このヘッダを返すように設定を追加して、エラーが発生しないことをやっと確認した。

    @Override
    public Navigation run() throws Exception {
        String json = service.getLatestPostIn10AsJson();
        requestScope("json", json);
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
        return forward("latest.jsp");
    }

サーバが返すレスポンスヘッダ

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With
Content-Length: 328909876
Server: Jetty(6.1.x)

*1:ただし、ブラウザがエラーを返すから書いているだけで、この記述を無視する Ajax クライアントを実行させることは可能である

*2:prototype.js も同様に上記のヘッダを追加するようだ