SvelteKit表单POST对接FastAPI OAuth2认证失败排查
SvelteKit表单POST认证请求FastAPI后端返回"field required"错误
问题详情
刚接触SvelteKit,使用表单POST action实现前端认证时遇到问题。相同的FastAPI后端代码在Next.js/React中可正常运行,但在SvelteKit中,后端返回“field required”错误,提示用户名和密码不能为空,怀疑问题出在fetch请求的body部分。
+page.server.js代码
import { fail, redirect } from '@sveltejs/kit'; /** @type {import('./$types').Actions} */ export const actions = { default: async ({ fetch, cookies, request }) => { const data = await request.formData(); const username = data.get('username'); const password = data.get('password'); if (!username || !password) { return fail(400, { username, password, missing: true }) } const response = await fetch(`${api_url}/login`, { method: "POST", headers: { 'Content-Type': 'application/json', "WWW-Authenticate": "Bearer", 'Accept': 'application/json' }, body: JSON.stringify({username, password}) }) const json = await response.json(); if (response.status === 200) { cookies.set('token', json.access_token, { path: '/', httpOnly: true, sameSite: 'strict', maxAge: 60 * 60 * 24 * 1000 }); throw redirect(302, "/"); } else { return fail(422, { errors: json }); } } }
错误信息
msg: "field required" type: "value_error.missing"
+page.svelte代码
<script> import { enhance } from '$app/forms'; /** @type {import('./$types').ActionData} */ export let form; </script> <!-- component --> <div class="w-full min-h-screen bg-cyan-950 text-white flex flex-col sm:justify-center items-center pt-6 sm:pt-0"> <div class="w-full sm:max-w-md p-5 mx-auto"> <h2 class="mb-12 text-center text-5xl font-extrabold">Accedi</h2> {#if form?.errors}<p class="error">{console.log(form.errors)}</p>{/if} <form method="POST" use:enhance> <div class="mb-4"> <label class="block mb-1" for="email">Username</label> <input id="username" type="text" name="username" class="text-gray-700 py-2 px-3 border border-gray-300 focus:border-red-300 focus:outline-none focus:ring focus:ring-red-200 focus:ring-opacity-50 rounded-md shadow-sm disabled:bg-gray-100 mt-1 block w-full" /> </div> <div class="mb-4"> <label class="block mb-1" for="password">Password</label> <input id="password" type="password" name="password" class="text-gray-700 py-2 px-3 border border-gray-300 focus:border-red-300 focus:outline-none focus:ring focus:ring-red-200 focus:ring-opacity-50 rounded-md shadow-sm disabled:bg-gray-100 mt-1 block w-full" /> </div> <div class="mt-6 flex items-center justify-between"> <div class="flex items-center"> <input id="remember_me" type="checkbox" class="border border-gray-300 text-red-600 shadow-sm focus:border-red-300 focus:ring focus:ring-red-200 focus:ring-opacity-50" /> <label for="remember_me" class="ml-2 block text-sm leading-5 text-gray-200"> Ricordami </label> </div> </div> <div class="mt-6"> <button class="w-full inline-flex items-center justify-center px-4 py-2 bg-cyan-600 border border-transparent rounded-md font-semibold capitalize text-white hover:bg-cyan-800 active:bg-red-700 focus:outline-none focus:border-red-700 focus:ring focus:ring-red-200 disabled:opacity-25 transition">Accedi</button> </div> </form> </div> </div>
FastAPI代码
@router.post("/login", response_model=Token, tags=["Auth"]) async def login_for_access_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()] ): user = authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"}
问题原因及解决方法
FastAPI的OAuth2PasswordRequestForm依赖默认只接受application/x-www-form-urlencoded格式的请求体,而当前SvelteKit后端请求用的是application/json格式,导致FastAPI无法解析到用户名和密码字段,返回"field required"错误。
修改方案
把+page.server.js中的fetch请求改成发送表单格式数据,替换原fetch请求部分:
const response = await fetch(`${api_url}/login`, { method: "POST", headers: { "WWW-Authenticate": "Bearer", 'Accept': 'application/json' // 移除Content-Type: application/json,浏览器会自动设置为正确的表单格式头部 }, body: new URLSearchParams({ username, password }) // 用URLSearchParams构造表单数据 })
额外说明
- Next.js/React中之前能正常工作,是因为当时的请求格式符合
OAuth2PasswordRequestForm的要求(大概率是发送了表单格式数据)。 - 若想让FastAPI支持JSON格式登录请求,可自定义Pydantic模型替代
OAuth2PasswordRequestForm,但保持现有后端代码的情况下,修改前端请求格式是最简单的解决方案。
内容的提问来源于stack exchange,提问作者Pankaspe
相关产品推荐
相关产品推荐

