中间件
非官方测试版翻译
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
您可以通过middleware()方法为整个路由器添加中间件。中间件将包裹过程调用,且必须透传其返回值。
授权验证
在以下示例中,任何对admin.*的调用都会在执行查询或变更前验证用户是否为"admin"。
tstrpc.router<Context>().query('foo', {resolve() {return 'bar';},}).merge('admin.',trpc.router<Context>().middleware(async (opts) => {if (!opts.ctx.user?.isAdmin) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next();}).query('secretPlace', {resolve() {return 'a key';},}),);
tstrpc.router<Context>().query('foo', {resolve() {return 'bar';},}).merge('admin.',trpc.router<Context>().middleware(async (opts) => {if (!opts.ctx.user?.isAdmin) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next();}).query('secretPlace', {resolve() {return 'a key';},}),);
技巧
关于上例中抛出的 TRPCError 的更多信息,请参阅错误处理。
日志记录
在以下示例中,查询操作的耗时会被自动记录到日志。
tstrpc.router<Context>().middleware(async ({ path, type, next }) => {const start = Date.now();const result = await next();const durationMs = Date.now() - start;result.ok? logMock('OK request timing:', { path, type, durationMs }): logMock('Non-OK request timing', { path, type, durationMs });return result;}).query('foo', {resolve() {return 'bar';},}).query('abc', {resolve() {return 'def';},});
tstrpc.router<Context>().middleware(async ({ path, type, next }) => {const start = Date.now();const result = await next();const durationMs = Date.now() - start;result.ok? logMock('OK request timing:', { path, type, durationMs }): logMock('Non-OK request timing', { path, type, durationMs });return result;}).query('foo', {resolve() {return 'bar';},}).query('abc', {resolve() {return 'def';},});
上下文替换
中间件可以替换路由器的上下文,后续过程将接收到新的上下文值:
tsinterface Context {// user is nullableuser?: {id: string;};}trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,user: opts.ctx.user, // user value is known to be non-null now},});}).query('userId', {async resolve({ ctx }) {return ctx.user.id;},});
tsinterface Context {// user is nullableuser?: {id: string;};}trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,user: opts.ctx.user, // user value is known to be non-null now},});}).query('userId', {async resolve({ ctx }) {return ctx.user.id;},});
createProtectedRouter()辅助函数
此辅助函数可在应用树任意位置使用,用于强制下游过程通过授权验证。
server/createRouter.tstsximport * as trpc from '@trpc/server';import { Context } from './context';export function createProtectedRouter() {return trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new trpc.TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,// infers that `user` is non-nullable to downstream proceduresuser: opts.ctx.user,},});});}
server/createRouter.tstsximport * as trpc from '@trpc/server';import { Context } from './context';export function createProtectedRouter() {return trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new trpc.TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,// infers that `user` is non-nullable to downstream proceduresuser: opts.ctx.user,},});});}
原始输入
中间件可以访问即将传递给过程的原始输入。这适用于需要访问过程输入的认证/预处理操作,与上下文替换结合使用时尤为有用。
注意
传递给中间件的rawInput尚未通过过程的input模式/验证器校验,使用时务必谨慎!因此rawInput的类型为unknown。更多信息参见#1059。
tsconst inputSchema = z.object({ userId: z.string() });trpc.router<Context>().middleware(async ({ next, rawInput, ctx }) => {const result = inputSchema.safeParse(rawInput);if (!result.success) throw new TRPCError({ code: 'BAD_REQUEST' });const { userId } = result.data;// Check user id authreturn next({ ctx: { ...ctx, userId } });}).query('userId', {input: inputSchema,resolve({ ctx }) {return ctx.userId;},});
tsconst inputSchema = z.object({ userId: z.string() });trpc.router<Context>().middleware(async ({ next, rawInput, ctx }) => {const result = inputSchema.safeParse(rawInput);if (!result.success) throw new TRPCError({ code: 'BAD_REQUEST' });const { userId } = result.data;// Check user id authreturn next({ ctx: { ...ctx, userId } });}).query('userId', {input: inputSchema,resolve({ ctx }) {return ctx.userId;},});