axiosでREST APIの通信を行う時にSafariではエラーが起きたときの対処法

皆さんこんにちは！

Windowsで開発している僕にとってはSafari特有のエラー程苦しむものは無いです。

ただ、この時代スマートフォンの使用率が圧倒的に高い10代、20代のほとんどはiPhoneを使っていると思います。

僕の周りでもiPhoneの使用率が圧倒的に高いです。

なので、iPhoneのエラーも無視するわけにはいかない。。。

今日はそんなこんなでaxiosでLaravelで作成したバックエンドとの通信でWindows環境で開発しているので、エラー内容は確認できなかったのですが、おそらくCORSエラーが原因でAPI通信を行うことができませんでした。

結論から言いますと、SafariではChromeのように上手くキャッシュの処理を行うことができないそうです。

なので、このキャッシュの問題を解決してあげることでSafariでもAPI通信を行うことができます！

それでは、説明を見ていきましょう！

#はじめに#

今回は、Vueでの説明となります。

もし、Reactなどを使っている場合は適時変更して下さい。

また、バックエンドではLaravelでCORSドメインの設定などを行います。

もし、Laravel以外でAPIを作成している場合も適時変更してください。

#axiosでCookie設定#

axiosのデフォルトでは、``withCredentials``というCookieを使えるようにするためのプロパティの設定が**OFF**になっています。

なので、これを**ON**にしてあげましょう！

```main.js
// axiosのプロトタイプ宣言
Vue.prototype.$axios = axios
// Cookieの使用を可とする
axios.defaults.withCredentials = true
```

これでaxiosがデフォルトでCookieを使用することができます。

#CORSドメイン設定#

次に、axiosでCookieの情報を受け取るためにバックエンドで適切なヘッダーを追加してあげましょう！

```
> php artisan make::middleware ApiCors
```

```Http/Middleware/ApiCors.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ApiCors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        // すべてのレスポンスに CORS 用のヘッダーを追加する必要はないので URL から判断する
        $paths = explode('/', $request->getPathInfo());
        if ($paths[1] === 'api') {
            return $next($request)
            ->header('Access-Control-Allow-Origin', config('cors.allowed_origins'))
            ->header('Cache-Control', 'no-cache max-age=3600')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Credentials', 'true')
            ->header('Access-Control-Allow-Headers', 'X-XSRF-TOKEN, Authorization, content-type, Transfer-Encoding, Accept, Accept-Encoding, Accept-Language');
        }
        return $next($request);
    }
}
```

``$paths = explode('/', $request->getPathInfo());``と``if ($paths[1] === 'api')``で`/api/apis```のようなAPI通信を行う時だけこのミドルウェアを適用するようにしています。

##Access-Control-Allow-Origin##

``Access-Control-Allow-Origin``ではCORSドメインを許可するドメインを登録しています。

例えば、``http://localhost:3000``からのリクエストを許可したい場合はここに``http://localhost:3000``と入力してください。

これを``*``のようにすると全てのドメインからリクエストが可能なので、データ抜き放題となるので注意して下さい。

##Access-Control-Allow-Methods##

``Access-Control-Allow-Methods``では、使用できるメソッドを書きます。ほとんどの場合は上記のようにしてOKです。

##Access-Control-Allow-Credentials##

``Access-Control-Allow-Credentials``を``true``にすることで、先ほどaxiosで設定したCookie情報のリクエストを受け取れるようにしています。

##Access-Control-Allow-Headers##

最後に、``Access-Control-Allow-Headers``を説明します。

これが重要です！

色々あるのですが2つだけ説明します。

``X-XSRF-TOKEN``はaxiosで発行したトークン情報です。PHPで言うと、``form``の``CSRF``のようなものです。これを受け取るためにこのヘッダーを追加します。

次に、``content-type``です。

これは、GETやHEADの場合は必要ないのですが、以下のようなリクエストを行う場合``content-type``は``applicatio/json``となります。

```
this.$axios
        .post(process.env.VUE_APP_LARAVEL_SITE_URL + '/api/participants', {
          name: 'akki'
        })
```

そして、``content-type``のデフォルトでは``application/json``は対応していません。

なので、このような``application/json``の形を受け取るのを許可するために``content-type``を追加します。

ちなみに場合によっては、``content-type``でエラーが出ます。その時は``Content-Type``と設定して見て下さい。

##ミドルウェアの適用##

最後に作成したこのミドルウェアを適用させましょう！

``App/Http/Middleware/Karnel.php``を開いてください。

```Karnel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        // CORSドメインの設定を追加
        \App\Http\Middleware\ApiCors::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];
    // 省略
}
```

必ず**$middleware**に追加してください！！

なぜかと言うと、$middlewareに追加しないと``OPTIONS``リクエストに今作成したミドルウェアを適用することができません！

絶対に**$middleware**に追加！！！

いかがだったでしょうか？

このエラーで3日程時間を費やされたので、本当に苦労しました。

ぜひ皆さんのお力になれたらなと思います。

以上、「axiosでREST APIの通信を行う時にSafariではエラーが起きたときの対処法」でした！

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

あと、最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。

気になる方はぜひ一度ご相談ください！

Thank you for reading