如何在NestJS拦截器中修改@Render装饰器接收的模板路径并添加多语言标识?
问题原因
你代码不生效的核心原因是:SetMetadata是Nest提供的装饰器函数,仅能在类、方法的定义阶段静态绑定元数据,运行时调用该函数无法修改已经挂载到控制器/方法上的元数据,自然不会生效。
正确实现方案
最安全的实现方式是利用Nest内置的渲染规则:当控制器返回的参数中包含view属性时,会直接覆盖@Render()装饰器中指定的模板路径,不需要修改静态元数据,也不会出现并发请求的元数据冲突问题。
实现代码
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { RENDER_METADATA } from '@nestjs/common/constants'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable() export class RenderByLanguageInterceptor implements NestInterceptor { constructor(private reflector: Reflector) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { // 拿到原本@Render装饰器中定义的模板名 const baseTemplate = this.reflector.getAllAndOverride<string>(RENDER_METADATA, [ context.getHandler(), context.getClass(), ]); // 获取当前请求的语言 const lang = context.switchToHttp().getRequest().i18nLang; // 校验lang是否为允许的取值,避免路径遍历漏洞 const allowedLangs = ['zh-CN', 'en-US', 'ja-JP']; const safeLang = allowedLangs.includes(lang) ? lang : 'zh-CN'; return next.handle().pipe( // 给控制器返回的视图参数追加view属性,覆盖原有模板路径 map((viewData = {}) => { return { ...viewData, view: `${safeLang}/${baseTemplate}`, }; }), ); } }
配套控制器写法
你的控制器不需要做太多修改,保留原有的@Render装饰器即可:
import { Controller, Get, Render } from '@nestjs/common'; @Controller() export class AppController { @Get() @Render('home') // 这里写基础模板名即可,拦截器会自动加上语言前缀 getHome() { return { title: '首页', }; } }
注意事项
- 如果你使用的是Fastify适配器,渲染规则和上述完全一致,不需要修改拦截器逻辑
- 务必对lang参数做合法性校验,避免用户构造特殊语言参数触发路径遍历漏洞,访问到项目外的文件
内容的提问来源于stack exchange,提问作者Jenya7771
相关产品推荐
相关产品推荐

