LoginSignup
2024_Hello_World
@2024_Hello_World

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Ajax通信ができない(AxiosErrorが起こる)

解決したいこと

データ分析画面で分析するデータの期間をフォームから選択して分析ボタン押すとAjax(Axios)通信によってその期間をコンソールに出力する。
その際にエラーが起こってしまいます。
解決方法を教えて下さい。

画面レイアウト

スクリーンショット 2024-06-05 121558.png

開発環境

フロントエンド 
vue.js 3.2
Inertia.js 1.0

バックエンド
Laravel 9.52.16

発生している問題・エラー

Analysis.vue?t=1717546741599:37 GET http://localhost:8000/api/analysis/?startDate=2024-06-18&endDate=2024-06-19&type=perDay 401 (Unauthorized)
AxiosError {message: 'Request failed with status code 401', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}

該当するソースコード

Analysis.vue

<script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import { Head } from '@inertiajs/vue3';
import { reactive, onMounted } from 'vue';
import { getToday } from '@/common';
import axios from 'axios';

//今日の日付を取得する
onMounted(() => {
    form.startDate = getToday()
    form.endDate = getToday();
})

//フォームの値を取得する
const form = reactive({
    startDate: null,
    endDate: null,
    type: 'perDay'
})

//分析ボタンを押したときにAjax通信が起こる
const getDate = async () => {
    try {
        const response = await axios.get('/api/analysis/', {
            params: {
                startDate: form.startDate,
                endDate: form.endDate,
                type: form.type
            }
        });
        console.log(response.data);
    } catch (error) {
        console.error(error);
    }
};

</script>

<template>
    <Head title="データ分析" />

    <AuthenticatedLayout>
        <template #header>
            <h2 class="font-semibold text-xl text-gray-800 leading-tight">データ分析</h2>
        </template>

        <div class="py-12">
            <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
                <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                    <div class="p-6 text-gray-900">
                        <form @submit.prevent="getDate">
                            From: <input type="date" name="startDate" v-model="form.startDate">
                            To: <input type="date" name="endDate" v-model="form.endDate"><br>
                            <button class="mt-4 flex mx-auto text-white bg-indigo-500 border-0 py-2 px-8 focus:outline-none hover:bg-indigo-600 rounded text-lg">分析する</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </AuthenticatedLayout>
</template>
api.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AnalysisController;

Route::middleware('auth:sanctum')
->get('/analysis', [ AnalysisController::class, 'index'])
->name('api.analysis');

↓こちらは元から変更していませんが念のため記載しておきます。

RouteServiceProvider.php
<?php

 /* ここまで省略
  RouteServiseProviderクラスのbootメソッド */

    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }
//以下省略

web.php
<?php

use App\Http\Controllers\AnalysisController;

Route::get('analysis', [AnalysisController::class, 'index'])->name('analysis');

AnalysisContoroller.php
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class AnalysisController extends Controller
{
    public function index(Request $request)
    {
        // dd関数が実行されなかった
        dd($request);
        return response()->json([
            'data' => $request->startDate
        ], Response::HTTP_OK);
    }
}

自分で試したこと

・DevToolsのconsoleタブ確認

スクリーンショット 2024-06-05 113203.png

・DevToolsのnetwookタブ確認

config→headerの中に X-XSRF-TOKEN(Inertia側のトークン)は確認できましたが、CSRF(Laravel側のトークン)は確認できなかった。

スクリーンショット 2024-06-05 113256.png

・AnalysisContoroller.phpでdd関数を記述
dd関数が実行されなかったので、コントローラーに渡される前のLaravel側の認証がうまくできてないのではないかと考えています。

・Chatgptで聞いてみました。
401エラーは認証エラーということがわかりました。
Chatgpが答えたコードを追記しました。
エラーは解消されませんでした。

スクリーンショット 2024-06-05 120033.png

スクリーンショット 2024-06-05 120123.png

1

2Answer

根本的な間違いが二つ混ざってる。
今の思い込みを捨てない限り絶対に解決しない。

・Laravelのapiルートは基本的には外部から利用するもの。
http://localhost:8000/ でLaravelもJSも動いてるプロジェクトの内部から利用するものではない。
ここ数年の初心者はみんな同じ勘違いしている。「APIはJSから使うもの」という完全に間違った思い込みを捨てられないので全部間違えてる。
apiはステートレス=クッキーもセッションも無効。
apiではクッキーはセットされないんだからヘッダーなんか見ても無駄。
例外は「SactumのSPA認証を使ってる場合」だけどInertiaなので一切関係ない。

Laravelはこの辺りの機能の組み合わせが複雑化しすぎているのでやりたいことに対して適切なスターターキットを選ばないとやりたいことができない。

・InertiaならFromヘルパーを活用する
axiosは不要。
https://inertiajs.com/forms
breezeでのLaravel公式の使い方を見ればform.postで送信してるだけと分かる。これでajaxでの送信と同じ動作になるのでaxiosで送信なんてしなくていい。Inertia内部でもaxiosを使っているので同じ。

const form = useForm({
    email: '',
    password: '',
    remember: false,
});

const submit = () => {
    form.post(route('login'), {
        onFinish: () => form.reset('password'),
    });
};

Inertiaを使うならルートはapiではなくwebを使う。
上の例でもroute('login')に送信している。これはweb側。
「Inertiaを使う」というのはBladeのviewをVueやReactに置き換えてるだけでLaravel側は今まで通り普通にwebルートを使うってこと。
Laravel側をモダンフロントに合わせて特殊なことしなくていいのがInertiaなんだからapiルートを使うような特殊なことしてる時点で間違えてる。
webルートなのでクッキーもセッションも有効でCSRFも認証も自動的に解決する。CORSなどのモダンフロント特有のあれこれを無視できるのがInertiaのメリット。

Inertiaを使ってるなら間違った思い込みで「axiosでapiに送信」なんてせずにInertiaの使い方を覚える。
本当にやりたいのは「axiosでapiに送信」ならInertiaを選んだことが間違いなのでプロジェクト作成からやり直す。

2Like

Comments

  1. 回答ありがとうございます!
    Inertiaのform.postを使いエラーが起こらずデータ送信できました。
    丁寧に教えていただきありがとうございました!

php 側の認証は cookie にキーを発行しているのではないでしょうか?
F12 で axios を使った通信上にて cookie はそのリクエストに含まれていますでしょうか?

0Like

Comments

  1. ご回答いただきありがとうございます!

    php 側の認証は cookie にキーを発行しているのではないでしょうか?

    →F12でアプリケーションタブを確認したところ、php側でもphp 側の認証は cookie にキーを発行できてるようでした。

    スクリーンショット 2024-06-05 135054.png

    F12 で axios を使った通信上にて cookie はそのリクエストに含まれていますでしょうか?

    →含まれていませんでした。
    スクリーンショット 2024-06-05 135549.png

    Axios通信時にPHP側のCookieもリクエストに渡せばいいということでしょうか?
    その方法について調べてみます。
    回答から分かることあればお手すきの際に教えていただけると幸いです。
    よろしくお願いいたします。

  2. いや、記事にある通り オプションに withCredentials:true をつけないと 現在のページの cookie がコピーされないという話ですよ!

  3. ご回答いただきありがとうございます!
    助言いただいたとおりオプションにwithCredentials:trueを付けたのですがCookieの付与がされていないようでした...
    ほかに試したほうが良いことなどあればご教授いただきたく思います。
    CSRFトークンなどLaravelの認証機能について調べてみようと思います。
    よろしくお願いいたします。

    スクリーンショット 2024-06-05 144506.png

  4. いや、F12のネットワークを確認してください。 axios が window側が持っているものを参照する様に指定して付与されるので
    image.png

  5. 記載の箇所を確認したところおっしゃるとおりCookie付与されていること確認できました!
    しかし、401ステータスは依然として解消されませんでした。
    Cookieのトークンでは認証されないということでしょうか?
    Laravel側でsanctumというAPI認証機能を使っているはずです。
    その機能がうまく適用されていないのではと考えています。
    何か解決に向けて糸口があればご教授いただければと思います。
    度々の質問になり恐縮ですがよろしくお願いいたします。

    スクリーンショット 2024-06-05 151823.png

  6. ここらへんの話?(構成知らないのでわからないですが

    In addition, you should enable the withCredentials and withXSRFToken options on your application's global axios instance. Typically, this should be performed in your resources/js/bootstrap.js file. If you are not using Axios to make HTTP requests from your frontend, you should perform the equivalent configuration on your own HTTP client:

    axios.defaults.withCredentials = true;
    axios.defaults.withXSRFToken = true;
    

Your answer might help someone💌