NextJS 13新API目录下RTK Query POST请求触发失败问题排查
NextJS 13中使用RTK Query发起POST请求失败的解决方法
问题场景
尝试在NextJS 13的新API目录下用RTK Query向后端发起POST请求,以下两种触发方式均失败:
// 第一种尝试 const dispatch = useDispatch<AppDispatch>(); dispatch(addMemoria.initiate({firstName, lastName, birthDate: selectedBirthDate, deathDate: selectedDeathDate})); // 第二种尝试 addMemoria.initiate({firstName, lastName, birthDate: selectedBirthDate, deathDate: selectedDeathDate});
预期是成功发送POST请求并在数据库新增Memoria条目,但出现如下错误:
hydration-error-info.js?32aa:27 An unhandled error occurred processing a request for the endpoint "addMemoria". In the case of an unhandled error, no tags will be "provided" or "invalidated". Error: Invariant: Method expects to have requestAsyncStorage, none available at headers (webpack-internal:///(:3000/e-memoria/app-client)/./node_modules/next/dist/client/components/headers.js:17:15) at getServerSession (webpack-internal:///(:3000/e-memoria/app-client)/./node_modules/next-auth/next/index.js:94:35) at prepareHeaders (webpack-internal:///(:3000/e-memoria/app-client)/./src/store/strapiApi.ts:23:90) at eval (webpack-internal:///(:3000/e-memoria/app-client)/./node_modules/@reduxjs/toolkit/dist/query/rtk-query.esm.js:239:42) at step (webpack-internal:///(:3000/e-memoria/app-client)/./node_modules/@reduxjs/toolkit/dist/query/rtk-query.esm.js:44:23) at Object.eval [as next] (webpack-internal:///(:3000/e-memoria/app-client)/./node_modules/@reduxjs/toolkit/dist/query/rtk-query.esm.js:25:53)
有效请求参考(HTTP客户端)
用HTTP客户端直接发起POST请求可以成功:
POST http://localhost:1337/api/memorias Content-Type: application/json authorization: Bearer **************************** { "data": { "firstName": "FirstNAmeTest", "lastName": "LAstNameTest", "birthDate": "2021-09-01", "deathDate": "2021-09-02" } }
当前配置代码
RTK Store配置
export const store = configureStore({ reducer: { strapiApi: strapiApi.reducer, }, middleware(getDefaultMiddleware) { return getDefaultMiddleware().concat(strapiApi.middleware); }, }); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch;
API端点配置(GET请求已正常授权)
const baseQuery = fetchBaseQuery({ baseUrl: process.env.STRAPI_API_URL + '/api/', //"http://localhost:3000/api/", prepareHeaders: async (headers, { getState }) => { const session : any = await getServerSession(authOptions); // 如果状态中有token,就添加到请求头 if (session) { headers.set('authorization', `Bearer ${session?.jwt}`) } headers.set('cache', 'no-store'); headers.set('Cache-control', 'no-store, max-age=0'); return headers }, }) export const strapiApi = createApi({ reducerPath: "strapiApi", baseQuery: baseQuery, tagTypes: ["Memoria", "bla"], endpoints: (builder) => ({ getAllMemorias: builder.query<{ results: Array<{ name: string }> }, void>({ query: () => `memorias`, providesTags: ['bla'], }), addMemoria: builder.mutation<Memoria, Partial<Memoria>>({ query: (body: Partial<Memoria>) => ({ url: `memorias`, method: 'POST', body: createStrapiPOSTBody(body), }), invalidatesTags: [{ type: 'Memoria', id: 'LIST' }], }), }); export const { getAllMemorias, addMemoria } = strapiApi.endpoints;
解决方法
1. 错误根源:客户端调用了服务器端方法
错误提示的requestAsyncStorage不可用,是因为你在客户端组件的prepareHeaders里调用了getServerSession——这个NextAuth方法只能在服务器组件、API路由或Server Actions中运行,不能在客户端环境执行。
2. 修改prepareHeaders逻辑,区分环境
需要拆分服务器端和客户端的token获取逻辑:
- 服务器端:继续用
getServerSession获取会话 - 客户端:通过
useSession(来自next-auth/react)获取token,并存入Redux状态后,从getState读取
修改后的baseQuery:
const baseQuery = fetchBaseQuery({ baseUrl: process.env.STRAPI_API_URL + '/api/', prepareHeaders: async (headers, { getState }) => { const rootState = getState() as RootState; // 从Redux状态读取客户端token(需提前通过useSession存入) const clientToken = rootState.auth?.jwt; // 服务器端环境 if (typeof window === 'undefined') { const session = await getServerSession(authOptions); if (session) { headers.set('authorization', `Bearer ${session.jwt}`); } } // 客户端环境,且有token时设置 else if (clientToken) { headers.set('authorization', `Bearer ${clientToken}`); } headers.set('cache', 'no-store'); headers.set('Cache-control', 'no-store, max-age=0'); return headers; }, });
3. 正确触发RTK Query Mutation的方式
不要手动用initiate+dispatch,RTK Query会自动为每个mutation生成对应的钩子(命名规则:use[EndpointName]Mutation),这是客户端组件触发mutation的标准方式,还会自动处理loading/error状态:
'use client'; import { useAddMemoriaMutation } from '@/src/store/strapiApi'; // 在客户端组件内使用 const AddMemoriaForm = () => { // 获取mutation方法和状态 const [addMemoria, { isLoading, error }] = useAddMemoriaMutation(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { // 调用mutation并处理结果 await addMemoria({ firstName, lastName, birthDate: selectedBirthDate, deathDate: selectedDeathDate }).unwrap(); // 请求成功后的操作,比如重置表单、提示成功 } catch (err) { console.error('添加Memoria失败:', err); } }; return ( <form onSubmit={handleSubmit}> {/* 表单内容 */} <button type="submit" disabled={isLoading}> {isLoading ? '提交中...' : '添加'} </button> {error && <p className="text-red-500">添加失败,请重试</p>} </form> ); }; export default AddMemoriaForm;
补充说明
- 确保你的组件是客户端组件(在NextJS 13中需要添加
'use client'指令在文件顶部) - 如果还没有Redux的auth slice存储token,可以在客户端组件中用
useSession获取后,通过dispatch存入store,或者直接在mutation的query方法中手动设置headers(临时可用,但推荐存入Redux统一管理)
内容的提问来源于stack exchange,提问作者Sergiu
相关产品推荐
相关产品推荐

