Server Actions调用路由处理器时Middleware无法识别Session的解决方案咨询
问题:Server Actions调用路由处理器时Session为undefined的解决方案
排查发现,使用Server Actions调用路由处理器时,Session会显示为undefined。原因是Server Actions在服务器端运行,不存在浏览器的请求上下文,导致基于Middleware的会话管理(如Cookie或Token)无法生效。请问是否有办法继续使用Server Actions调用路由处理器并通过Middleware进行保护,还是需要完全弃用Server Actions直接调用路由处理器?
middleware.js代码
// middleware.ts import { NextResponse } from "next/server"; import createMiddleware from "next-intl/middleware"; import { routing } from "./i18n/routing"; import { decrypt, encrypt } from "@/utils/session"; // Next-Intl Middleware const intlMiddleware = createMiddleware(routing); // Authentication Middleware const authMiddleware = async (request) => { const path = request.nextUrl.pathname; // Skip auth check for non-protected routes if (!path.match(/\/(ar|en)\/(quizzes|admin)/)) { return NextResponse.next(); } const sessionCookie = request.cookies.get("session")?.value; console.log(sessionCookie); // If there is no session, redirect to login if (!sessionCookie) { return NextResponse.redirect(new URL("/ar/login", request.nextUrl)); } try { // Decrypt session to check validity const session = await decrypt(sessionCookie); if (!session?.userId) { const response = NextResponse.redirect( new URL("/ar/login", request.nextUrl) ); response.cookies.delete("session"); return response; } return NextResponse.next(); } catch (err) { const response = NextResponse.redirect( new URL("/ar/login", request.nextUrl) ); response.cookies.delete("session"); return response; } }; // Session Refresh Middleware const updateSessionMiddleWare = async (request) => { const session = request.cookies.get("session")?.value; if (!session) return null; // Refresh the session expiration const parsed = await decrypt(session); const token = await encrypt({ userId: parsed.userId }); const response = NextResponse.next(); response.cookies.set({ name: "session", value: token, httpOnly: true, secure: process.env.NODE_ENV === "production", // Ensure cookie is only sent over HTTPS in production sameSite: "Lax", // Allow cross-origin requests maxAge: 60 * 60, // 1 hour in seconds path: "/", // Ensure the cookie is available to all routes }); return response; }; // Combined Middleware export default async function middleware(request) { const intlResponse = intlMiddleware(request); const sessionResponse = await updateSessionMiddleWare(request); const authResponse = await authMiddleware(request); if (authResponse.status === 307) { return authResponse; } if (sessionResponse) { const sessionCookie = sessionResponse.cookies.get("session"); if (sessionCookie) { intlResponse.cookies.set(sessionCookie); } } // Return the final response return intlResponse; } // Matcher Configuration export const config = { matcher: ["/", "/(ar|en)/:path*"], };
客户端组件请求代码
import { generateQuiz } from "./action"; const handleGenerate = async () => { const data = await generateQuiz(locale, quiz, requestBody); };
generateQuiz Server Action代码
"use server"; import { fetchData } from "@/utils/fetchData"; // Mark this as a Server Action export async function generateQuiz(locale, quiz, requestBody) { const response = await fetchData( `http://localhost:3000/${locale}/quizzes/initiate-quiz/${quiz}/api`, "POST", requestBody ); return response; }
fetchData函数代码
export const fetchData = async (url, method, body = null) => { try { const options = { method: method, headers: { "Content-Type": "application/json", }, credentials: "include", // Ensure the session cookie is sent along with the request }; // Only include the body if the method is not GET or HEAD if (method !== "GET" && method !== "HEAD" && body !== null) { options.body = JSON.stringify(body); } const response = await fetch(url, options); if (!response.ok) { return false; } return await response.json(); } catch (error) { return false; } };
路由处理器POST请求代码
export async function POST(request, { params }) { const session = await verifySession(); console.log(session); // 从Server Action调用时为undefined,其他情况正常识别 const t = await getTranslations("HomePage"); }
解决方案
不需要弃用Server Actions,以下两种方案均可解决问题:
方案1:抽离业务逻辑,跳过内部HTTP请求
Server Action本身运行在服务器端,无需额外发起HTTP请求调用路由处理器。将路由处理器中的核心业务逻辑(如会话验证、Quiz生成逻辑)抽成独立函数,在Server Action和路由处理器中直接复用即可。
示例代码:
// 抽离的通用业务逻辑函数 export async function handleInitiateQuiz(params, requestBody) { const session = await verifySession(); if (!session) throw new Error("未授权"); // 这里编写生成Quiz的核心逻辑 const t = await getTranslations("HomePage"); return { /* 返回处理结果 */ }; } // 路由处理器中调用 export async function POST(request, { params }) { const body = await request.json(); const result = await handleInitiateQuiz(params, body); return Response.json(result); } // Server Action中调用 "use server"; export async function generateQuiz(locale, quiz, requestBody) { return handleInitiateQuiz({ quiz }, requestBody); }
方案2:在Server Action中手动传递会话Cookie
如果必须保留内部fetch的调用方式,可以从Server Action的请求上下文获取会话Cookie,手动添加到fetch请求头中。
修改Server Action代码:
"use server"; import { cookies } from "next/headers"; import { fetchData } from "@/utils/fetchData"; export async function generateQuiz(locale, quiz, requestBody) { // 从Server Action上下文获取session cookie const sessionCookie = cookies().get("session")?.value; const response = await fetchData( `http://localhost:3000/${locale}/quizzes/initiate-quiz/${quiz}/api`, "POST", requestBody, sessionCookie // 新增传递session参数 ); return response; }
修改fetchData函数:
export const fetchData = async (url, method, body = null, sessionCookie = null) => { try { const options = { method: method, headers: { "Content-Type": "application/json", // 手动添加Cookie头 ...(sessionCookie && { Cookie: `session=${sessionCookie}` }), }, }; if (method !== "GET" && method !== "HEAD" && body !== null) { options.body = JSON.stringify(body); } const response = await fetch(url, options); if (!response.ok) { return false; } return await response.json(); } catch (error) { return false; } };
方案说明
- 方案1更高效,避免了不必要的内部HTTP请求,是优先推荐的做法。
- 方案2适合无法快速重构业务逻辑的场景,且Middleware依然会对内部请求生效,符合权限保护需求。
内容的提问来源于stack exchange,提问作者aasem shoshari
相关产品推荐
相关产品推荐

