はじめに
- Next.js App Routerの機能である、Route Handlersと標準のfetch APIを使ってファイル送信を実装した
- 調査に苦労したので備忘録としてまとめた
- Next.jsのバージョンは15.2.4。App Routerを使う
※これは、Pages Routerの時はAPI Routesと呼ばれていた機能に当たる
やりたいこと
- フロントエンドをNext.js、バックエンドをC#で実装し、ファイルの送信を実装する
- ユーザー入力部分からNext.jsのRoute Handlersに向かってリクエストを投げる
→ Route HandlersからC#に向けて再度リクエストを投げる、という流れ - ブラウザ標準のfetch APIを使用する
- 狙い:Route Handlersを中間APIとして使用することで、セキュリティ強化したい
実装方法
-
フロントエンドのリクエスト先をRoute Handlersにする
-
nextUrlの引数にNext.jsの中間APIを叩くURLを渡すイメージ
-
Jsonを送信する場合はContent-typeを指定する
page.tsxexport async function uploadFile( nextUrl: string, fileList: FileList | null ): Promise<Response> { if (!fileList || fileList.length === 0) { return Response.error(); } const formData = new FormData(); for (const file of fileList) { formData.append("upfiles", file, file.name); } // formDataを送信するときはContent-typeを指定しない const config: RequestInit = { method: "POST", body: formData, }; return await fetch(nextUrl, config);; }
-
Route Handlersにバックエンドへfetchするように記述する
- このとき、FormDataで送りたい場合はnew Headers()する(ここがポイント)
- そうではない場合、フロントエンドのリクエストのHeadersを流用する(ここもポイント)
- フロントエンド用のリクエストボディを作成するときにCookie等の情報を足す
src/api/backend/[...backendUrl]/route.tsexport async function POST(request: NextRequest): Promise<Response> { const isMultiPartFormData = request.headers.get("content-type")?.includes("multipart/form-data") ?? false; const bodyForBackEnd = isMultiPartFormData ? await request.formData() : await request.text(); // ファイル送信では新たにHeaderを作成し、その他ではそのまま流用する const headersForBackEnd = isMultiPartFormData ? new Headers() : request.headers; // ここでバックエンドに向けたURLを作成 const targetUrl = getTargetUrl(request.url); const url = new URL(targetUrl, baseUrl).href; console.debug("headersForBackend:", headersForBackEnd) const config : RequestInit= { method: "POST", headers: headersForBackEnd, body: bodyForBackEnd, credentials : 'include' as RequestCredentials, } // バックエンドに向けてfetchする const backendResult = await fetch(url, config); // クライアント表示用のResponseをnewして返却 // ここで足したい情報があれば足す const resForClient = new Response(backendResult.body) return resForClient; }
備考
- Headersをnewする/しない、newするとしたらrequest.headersを渡すか渡さないかによって、UND_ERR_REQ_CONTENT_LENGTH_MISMATCHが発生する
- どうもUndici側のエラーのようで、以前はバージョンアップで解決していた様子
- 今回は最新バージョンのNext.jsを使用しているため、解決方法がわからず、色々試してこうなった