NextJS服务器端操作登录后重定向至/app失败问题
登录后重定向至/app失效问题
我用Server Side Action实现了简易登录页面,登录成功创建会话后,本该重定向到/app路径,但功能失效。具体表现:
- 向
auth/login发送的POST请求返回303响应,但页面提示404 page not found - 服务器日志显示登录完成,且
/app布局的日志已输出,但页面只重新加载了登录页
服务器日志
3:I["(app-pages-browser)/./node_modules/next/dist/client/components/layout-router.js",["app-pages-internals","static/chunks/app-pages-internals.js"],""] 4:I["(app-pages-browser)/./node_modules/next/dist/client/components/render-from-template-context.js",["app-pages-internals","static/chunks/app-pages-internals.js"],""] 1:D{"name":"","env":"Server"} 2:D{"name":"AppPage","env":"Server"} 5:D{"name":"AppRootLayout","env":"Server"} 6:D{"name":"NotFound","env":"Server"} 6:["$","title",null,{"children":"404: This page could not be found."},["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]] 7:D{"name":"","env":"Server"} 0:["development",[[["",{"children":["(app)",{"children":["app",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true]}],["",{"children":["(app)",{"children":["app",{"children":["__PAGE__",{},[["$L1","$L2"],null],null]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","(app)","children","app","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}],null]},["$L5",null],null]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$6","notFoundStyles":[],"styles":[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/app/(app)/layout.css?v=1717519684402","precedence":"next_static/css/app/(app)/layout.css","crossOrigin":"$undefined"}]]}],null],[null,"$L7"]]]]] 2:["$","div",null,{"children":"test"}] 7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Pro Writing Buddy"}],["$","link","3",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"16x16"}]] 1:null 8:I["(app-pages-browser)/./src/app/(app)/components/Navigation.tsx",["app/(app)/layout","static/chunks/app/(app)/layout.js"],"default"] 9:D{"name":"Usage","env":"Server"} 9:["$","div",null,{"className":"p-5 rounded-lg bg-gradient-to-br from-violet-500 to-purple-500 text-white","children":[["$","div",null,{"className":"font-semibold","children":"Usage"}],["$","div",null,{"className":"my-2","children":["$","div",null,{"className":"w-full h-2 rounded-lg bg-white overflow-hidden","children":["$","div",null,{"className":"bg-slate-800 h-full","style":{"width":"50%"}}]}]}],["$","div",null,{"children":"5,000 / 10,000 credits used"}],["$","div",null,{"className":"mt-5 bg-white rounded-md text-gray-900 text-center py-3 px-5","children":"Upgrade now"}]]}] a:D{"name":"NotFound","env":"Server"} b:{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"} c:{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"} d:{"display":"inline-block"} e:{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0} a:[["$","title",null,{"children":"404: This page could not be found."},["$","div",null,{"style":"$b","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$c","children":"404"}],["$","div",null,{"style":"$d","children":["$","h2",null,{"style":"$e","children":"This page could not be found."}]}]]}]}]] 5:["$","html",null,{"lang":"en","children":["$","body",null,{"className":"w-screen h-screen overflow-hidden","children":["$","div",null,{"className":"flex h-full","children":[["$","div",null,{"className":"w-3/12 border-r border-gray-300","children":["$","div",null,{"className":"flex flex-col h-full","children":[["$","nav",null,{"className":"p-5","children":["$","$L8",null,{}]}],["$","div",null,{"className":"mt-auto p-5","children":"$9"}]]}]}],["$","div",null,{"className":"w-9/12 p-10","children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","(app)","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$a","notFoundStyles":[],"styles":null}]}]]}]}]}]
相关代码
/app/pages/auth/login.tsx
export default function LoginPage() { const { pending } = useFormStatus(); const [state, formAction] = useFormState<{ errors: {[key: string]: string[]} }, FormData>(login, initialState); return ( <form action={formAction}> .... </form> ) }
/app/actions/login.ts
export const login = async (prevState: any, formData: FormData): Promise<any> => { ... login logic redirect(`/app`, RedirectType.push); }
解决方法
- 检查路由结构:确认
/app路径对应的页面文件存在,比如/app/page.tsx或者路由分组(app)下的/app/(app)/page.tsx,文件名必须是page.tsx,符合Next.js App Router规范。 - 修正重定向路径:如果
/app是路由分组的名称,实际访问路径可能是根路径/,此时应将重定向改为redirect('/', RedirectType.push);如果/app是独立路由,确保/app/page.tsx存在。 - 调整Form State使用:使用
useFormState时,重定向需要直接抛出,不要返回任何状态数据。可以尝试暂时移除useFormState,直接用action={login}测试重定向是否正常,再逐步恢复状态逻辑。 - 清除缓存:开发环境下清除浏览器缓存或使用无痕模式,避免旧路由缓存导致的问题。
内容的提问来源于stack exchange,提问作者user25417500
相关产品推荐
相关产品推荐

