我正在我的 TRPC Nextjs 应用程序中构建基于令牌的身份验证。我将刷新令牌存储在用户 cookie 中。如果请求因访问令牌过期而失败,我想访问我的 API 路由以生成新的访问令牌。我无法理解如何在 TRPC 获取中访问 cookie(在我的情况下是刷新令牌)针对每个请求运行的处理程序。 (它显然运行客户端)
import { httpBatchLink, loggerLink } from "@trpc/client";
import { createTRPCNext } from "@trpc/next";
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
import superjson from "superjson";
import { type AppRouter } from "@/server/api/root";
export let token: string;
export const setToken = (newToken: string) => {
token = newToken;
};
{... ...}
export const api = createTRPCNext<AppRouter>({
config({ctx}) {
return {
links: [
loggerLink({
...
}),
httpBatchLink({
transformer: superjson,
url: `${getBaseUrl()}/api/trpc`,
headers: ({
}) => {
const newHeaders = new Headers();
if(token) newHeaders.set("Authorization", `Bearer ${token}`);
return newHeaders;
},
fetch(url, options) {
//I need to access the cookies here
//Ctx is undefined
return fetch(url, {
...options,
credentials: "include",
});
},
}),
],
};
},
ssr: false,
transformer: superjson,
});
{... ...}
我尝试使用像
js-cookie
这样的包,但这不起作用,因为代码不在实际的 React 组件中运行。我还尝试访问包含在 config({ctx})
中传递的标头的 ctx,但它始终是未定义的
在页面路由器中,我们只能在
headers
或getServerSideProps
中访问getInitialProps
。如果您想使用 tRPC 访问标头,您应该启用其 ssr
标志,如官方文档中所述:https://trpc.io/docs/client/nextjs/ssr
会是这样的:
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import { ssrPrepass } from '@trpc/next/ssrPrepass';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
ssr: true, // enabled it here
ssrPrepass,
config(opts) {
const { ctx } = opts;
if (typeof window !== 'undefined') {
// during client requests
return {
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
headers() {
// access headers here:
if (!ctx?.req?.headers) {
return {};
}
// To use SSR properly, you need to forward client headers to the server
// This is so you can pass through things like cookies when we're server-side rendering
return {
cookie: ctx.req.headers.cookie,
};
},
}),
],
};
},
});
但是,也有人说:
启用 SSR 时,tRPC 将使用
预取服务器上的所有查询。当您使用getInitialProps
getServerSideProps
时,这会导致像this这样的问题,而解决它是我们无法解决的。或者,您可以禁用 SSR(默认)并使用 Server-Side Helpers 预取
或getStaticProps
中的查询。getServerSideProps
因此推荐的解决方案是使用服务器端助手,可以在
getServerSideProps
函数中完成(示例来自官方文档 https://trpc.io/docs/client/nextjs/server-side-helpers#nextjs-示例):
import { createServerSideHelpers } from '@trpc/react-query/server';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
import { appRouter } from 'server/routers/_app';
import superjson from 'superjson';
import { trpc } from 'utils/trpc';
export async function getServerSideProps(
context: GetServerSidePropsContext<{ id: string }>,
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: {},
transformer: superjson,
});
const id = context.params?.id as string;
/*
* Prefetching the `post.byId` query.
* `prefetch` does not return the result and never throws - if you need that behavior, use `fetch` instead.
*/
await helpers.post.byId.prefetch({ id });
// Make sure to return { props: { trpcState: helpers.dehydrate() } }
return {
props: {
trpcState: helpers.dehydrate(),
id,
},
};
}
export default function PostViewPage(
props: InferGetServerSidePropsType<typeof getServerSideProps>,
) {
const { id } = props;
const postQuery = trpc.post.byId.useQuery({ id });
if (postQuery.status !== 'success') {
// won't happen since the query has been prefetched
return <>Loading...</>;
}
const { data } = postQuery;
return (
<>
<h1>{data.title}</h1>
<em>Created {data.createdAt.toLocaleDateString()}</em>
<p>{data.text}</p>
<h2>Raw data:</h2>
<pre>{JSON.stringify(data, null, 4)}</pre>
</>
);
}