💨

[Laravel] multipart/form-dataを送ったら400になった話

に公開

はじめに

フロントエンドでNuxt、バックエンドでLaravelを使って個人開発をしています。
フロントエンドからmultipart/form-dataPUTメソッドで送ったところ、Laravel側でリクエストが空と判定され、400 Bad Requestになっていました。

その時に起こったこと、解決策などをまとめていきます。

環境

Nuxtのバージョン ‥‥ 3.17.6
Laravelのバージョン ‥‥ 12
PHPのバージョン ‥‥ 8.4

起きたこと

画像データを扱う関係上、formDataを使ってデータを送いました。

実際の送信部分はこんな感じです。

  async update(
    data: UpdateRequest | FormData,
    options: UseFetchOptions<void> = {},
  ) {
    const defaultOptions: UseFetchOptions<void> = {
      method: 'PUT',
      body: data,
    }
    return await useApiAuthFetch(`/api/hogehoge`, {
      ...defaultOptions,
      ...options,
    })
  },

その結果ですが、タイトルにもあるように400が返ってきました。
調べていると、どうやら送信したリクエスト自体が空になっていそうでした。

原因

結論から言うと、PHPの仕様的な問題です。

SAPIレイヤーでは、POSTかつContent-Type: multipart/form-dataの場合にのみ、以下を行います。

  1. ボディをboundaryごとに分割
  2. テキスト部分は$_POSTへ入力
  3. ファイル部分は一時ファイルに保存し、$_FILESへ入力

これをしてくれるため、Laravel側では$request->input()$request->file()を使えます。

PUT でmultipart/form-dataを送ると、PHPはphp://inputに生データをそのまま渡すだけで、自動パースを行いません。
そのため Laravel側の$request->input()$request->file()は空になり、結果的にリクエスト不備として400 Bad Requestが返っていました。

PHPの仕様的な問題ですが、multipart/form-dataはそもそもPOSTメソッドが前提として作られてるので、一概にPHPが悪いわけではないのでご注意を。。。

解決策

multipart/form-dataで送る必要がある場合、更新処理はPUT/PATCHメソッドを使わず、POSTメソッドで送り、_method=PUTを追加します。

formData.append(`_method`, `PUT`)

Laravel はこの_methodを見て内部的にPUTメソッドとして処理するため、ルート定義はそのままRoute::put(...)を使えます。

備考

https://datatracker.ietf.org/doc/html/rfc7578
https://qiita.com/tsuchinoko0102/items/2f6c5b2a6163c1cc5894

Discussion