【Lumen】Lumen製APIに対してクロスドメインアクセスを行う（CORS）


# 異なるドメインからアクセスしようとするとエラーが返される

異なるドメインでアクセスしようとすると以下のようなエラーが出るのは周知のことかと思います。（クロスドメイン制約）

```
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://★★★★★★★★★★' is therefore not allowed access.
```

クロスドメインアクセスを行う場合は[CORS（Cross-Origin Resource Sharing）](https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control)の仕組みを使用する必要がある

# LumenでCORSを使用する

以下のファイルを`\app\Http\Middleware`に作成する。

```.php:CorsMiddleware.php
<?php

namespace App\Http\Middleware;
use Closure;

class CorsMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // TODO:Access-Control-Allow-Originを適切に指定する必要がある
        $headers = [
            'Access-Control-Allow-Origin'      => '*',
            'Access-Control-Allow-Methods'     => 'POST, GET, OPTIONS', // TODO:この部分も適切に設定する必要がある
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Max-Age'           => '86400',
            'Access-Control-Allow-Headers'     => 'Content-Type, Authorization, X-Requested-With'
        ];

        // preflightリクエスト用
        if ($request->isMethod('OPTIONS'))
        {
            return response()->json('{"method":"OPTIONS"}', 200, $headers);
        }

        $response = $next($request);
        foreach($headers as $key => $value)
        {
            $response->header($key, $value);
        }

        return $response;
    }
}
```

そして、`\bootstrap\app.php`に以下を追加する。

```.php:app.php
$app->middleware([
    App\Http\Middleware\CorsMiddleware::class
 ]);
```

これで、ヘッダーに`Access-Control-Allow-Origin`が追加されてクロスドメイン間でもアクセスを行うことができる。

もし本番と開発環境で切り分ける必要があるなら`.env`ファイルに書いて読み込むようにしておけばいいんじゃないですかね。

```.env.example:.env.example
ACCESS_CONTROL_ALLOW_ORIGIN=*
```

ちなみにpreflightリクエストとは目的の通信をする前に事前に安全かどうかを確かめるリクエストのことをさします。

> 「プリフライト」リクエストは始めに OPTIONS メソッドによるリクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうかを確かめます。サイト間リクエストはユーザーデータに影響を与える可能性があるため、このようにプリフライトを行います。

引用：[オリジン間リソース共有 (CORS) - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control)


# Basic認証をかけているサーバーにあるAPIに対してアクセスをする場合


`Access-Control-Allow-Origin`にワイルドカード`*`を指定してると以下のようなエラーが出る。

```
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
Origin 'http://★★★★★★★★★★' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
```

ようはちゃんと指定して書けといわれてるので`Access-Control-Allow-Origin`にちゃんとアクセス先を記述する必要がある。

```.php:CorsMiddleware.php
'Access-Control-Allow-Origin'      => 'http://★★★★★★★★★★',
```

あとクレデンシャルが必要な場合は`Access-Control-Allow-Credentials`も`true`にしておくこと。

>クレデンシャル・・・ユーザーの認証に用いられる情報の総称。ここではCookieやベーシック認証のID、パスをさす。

```.php:CorsMiddleware.php
'Access-Control-Allow-Credentials' => 'true',
```

また、JavaScriptからAjaxで通信する際にクレデンシャルが必要な場合はJavaScript側でオプション設定する必要があります。
JavaScript（ここではjQuery）側のオプション設定を載せておきます。

```post.js
        function sendPostToApiByAjax()
        {
            $.ajax({
                type:     "POST",
                url:      "http://★★★★★★★★★★",
                // username: "*****", // Basic認証用ユーザー名
                // password: "*****", // Basic認証用パスワード
                data: {
                    name : 'ajaxAPI' // リクエストパラメータ
                },
                xhrFields: {
                    withCredentials: true // クレデンシャル送るよオプション
                },
            }).done(function(data) {

                console.log('Successful communication with API!');

            }).fail(function(data) {
                console.log('Ajax fail (communication error)');
            });
        }
```

# おわり
- [CORS](https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control)むずかしい
- Laravelは[barryvdh/laravel-cors](https://github.com/barryvdh/laravel-cors)を入れたらよさげ（Lumenも対応してるみたいですがうまくいかなかった・・・）

# 参考

- [How To Resolve No 'Access-Control-Allow-Origin' Header In Lumen](https://seegatesite.com/how-to-resolve-no-access-control-allow-origin-header-in-lumen/)

以下CORSに関して参考させていただいたサイト

- [オリジン間リソース共有 (CORS) - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control)
- [CORSまとめ - Qiita](https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34)
- [CORSリクエストでクレデンシャル(≒クッキー)を必要とする場合の注意点 - Qiita](https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2)
- [CORS(Cross-Origin Resource Sharing)について - ぺーぺーSEのブログ](http://blog.pepese.com/entry/2016/02/02/144143)
- [CORSリクエストについてのまとめ - TOEIC940点の文系プログラマー](http://uraway.hatenablog.com/entry/2016/01/04/144531)