🍣

Inertia.jsで、PUTするとき、ファイルが送れない

2024/03/19に公開

やりたいこと

PUTメソッドで、ファイルを送って、それをバックエンドで保存したい。
laravel側のrouter

Route::put('settings', [SettingController::class, 'update'])->name('setting.update');

発生した問題

react側

const { data, setData, post, put, errors, processing, setError }= useForm<User>({...auth.user});

const submit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (processing) return;

    put(route("setting.update"), {
        onSuccess: () => {  
            console.log("変更を保存しました。");
        },
    });
};

これで、putすると、データを送っていても、dataを送っているのに、
Laravel側のValidationに引っかかって、データがないと怒られる。

原因

https://inertiajs.com/file-uploads

公式ドキュメントにて、

Multipart limitations
Uploading files using a multipart/form-data request is not natively supported in some server-side frameworks when using the PUT,PATCH, or DELETE HTTP methods. The simplest workaround for this limitation is to simply upload files using a POST request instead.

However, some frameworks, such as Laravel and Rails, support form method spoofing, which allows you to upload the files using POST, but have the framework handle the request as a PUT or PATCH request. This is done by including a _method attribute in the data of your request.

という記載を発見。

要するに、PUTやDELETEメソッドでは、ファイルが送れないらしい。
でも、LaravelやRailsでは、method spoofingなるものを使って、POSTで送信しながらも、PUTとしてLaravel側で受け取ってもらう方法がある模様。
そのためには、
「_method:put」を、requestのデータに含めて送ればいいらしい。

対応

const { data, setData, post, put, errors, processing, setError }= useForm<User & {
    _method: "put";
}>({...auth.user,_method: "put",});

const submit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (processing) return;

    post(route("setting.update"), {
        onSuccess: () => {  
            console.log("変更を保存しました。");
        },
    });
};

のように変えればOK。
これで、putのフリをしながら、postで送って、ファイルが送れるようになる。

変更点①

const { data, setData, post, put, errors, processing, setError }= useForm<User & {
    _method: "put";
}>({...auth.user,_method: "put",});

で、dataに、_method:"put"を渡している。

変更点②

post(route("setting.update"), {
        onSuccess: () => {  
            console.log("変更を保存しました。");
        },
    });

putではなく、postで送っている。
なお、Laravel側でroute("setting.update")は、変わらずputで定義している。

Discussion