8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React + FastAPI】OAuth2を利用した認証リクエスト時に出た442エラーの原因と解決方法

Last updated at Posted at 2025-02-09

はじめに

  • 個人の学習時の備忘録目的のため、シンプルな記事です
  • コード例は実際のコードから必要と思われる部分を抜粋しています
  • もし同じエラーで困っている人がいれば、何かの参考になれば幸いです

背景

  • フロントにReact、バックエンドにFastAPIを用いたアプリを作成中
  • 認証機能については、FastAPI側は公式チュートリアルに沿った形でOAuth2PasswordBearer(OAuth2)を使用して作成し、SwaggerUI上にてusernameとpasswordで認証できる事を確認済み

問題

React側からFastAPIのエンドポイントに対してusernameとpasswordで認証できるか試したところ、HTTPステータスコード 422 Unprocessable Entityというエラーになった。

原因

FastAPI側の認証のエンドポイントではOAuth2PasswordRequestForm(OAuth2)を使用しており、リクエストのデータ形式がフォームデータ(application/x-www-form-urlencoded)である必要がある。
しかし、React側からdataをJSON形式(application/json)で送信していたため、エラーになっていた。

例えば、OAuth2仕様が使用できる方法の1つ(「パスワードフロー」と呼ばれる)では、フォームフィールドとしてusernameとpasswordを送信する必要があります。
仕様では、フィールドの名前がusernameとpasswordであることと、JSONではなくフォームフィールドとして送信されることを要求しています。

解決方法

  • React側で、認証リクエスト時のみContent-Typeにapplication/jsonではなく、application/x-www-form-urlencodedを指定するように変更(条件分岐)した。

  • データ形式もJSONからURLエンコードへ変更する必要がある。
    →通常はURLSearchParams等を使用する。
    →今回はHTTPリクエストにAxiosを使用しており、AxiosではContent-Typeにapplication/x-www-form-urlencodedを指定すればデータが自動的にapplication/x-www-form-urlencodedの形にシリアライズされる。そのため、コード自体はJSON形式のままでOK

🆕 Automatic serialization
Axios will automatically serialize the data object to urlencoded format if the content-type header is set to application/x-www-form-urlencoded.

変更後のコード(React)

Requests.jsx
import axios from "axios";

const apiBaseUrl = import.meta.env.VITE_API_BASE_URL;

const apiPathUrl = {
  signup: "/auth/signup",
  signin: "/auth/signin",
};

// APIリクエストのベース
const apiRequest = async({ method, apiEndpoint, data={}, token, isFormUrlEncoded = false }) => {
  try {
    const headers = {
      ...(isFormUrlEncoded
        ? { "Content-Type": "application/x-www-form-urlencoded" } // Formで送信したい時(signup)に使用
        : { "Content-Type": "application/json" }),
      ...(token && { Authorization: `Bearer ${token}` }), // トークンが存在する場合のみ設定
    };

    const response = await axios({
      method: method,
      url: apiBaseUrl + apiEndpoint,
      data: data,
      headers: headers,
      });
    return response;
  } catch (error) {
    console.log('apiRequest failed', error);
    throw error;    
  }
};


// サインアップ
export const signupRequest = async(name, email, password) => {
  const response = await apiRequest({
    method: "POST", 
    apiEndpoint: apiPathUrl.signup, 
    data: {
      name: name,
      email: email,
      password: password,
    }, 
  });
  return response;
};

// サインイン
// メールアドレスでログインさせたいが、バックエンド側のOAuth2PasswordBearerが
// usernameを受け取る必要があるため、メールアドレスをusernameとして送信する
// OAuth2PasswordRequestForm は Formデータを受け取る仕様
// →application/x-www-form-urlencodedを指定すれば、dataはaxiosで自動的に対応形式にシリアライズされる
export const signinRequest = async(email, password) => {
  const response = await apiRequest({
    method: "POST", 
    apiEndpoint: apiPathUrl.signin, 
    data: {
      username: email,
      password: password,
    },
    isFormUrlEncoded: true,
  });
  return response;
};

補足メモ

application/x-www-form-urlencodedapplication/jsonの違い

項目 application/x-www-form-urlencoded application/json
データ形式 key1=value1&key2=value2 {"key1": "value1", "key2": "value2"}
エンコード URLエンコード JSONエンコード
用途 フォームデータ APIのリクエスト/レスポンス

おわりに

  • FastAPIはSwaggerUIで動作確認ができる点が便利だが、その分フレームワーク側でうまく処理されて見えにくくなる部分があることを再認識した。
  • APIリクエストのデータ形式について、あまり深く考えずにapplication/jsonとしていたので、フォームデータが必要になる場面がある事、正しく送信しないとエラーになるという事がわかった。

参考

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?