NestJS如何根据Accept请求头动态返回JSON或HBS渲染结果
实现方案
首先先修正现有代码的一个错误:你代码中判断headers.accept === "accept/json"是不符合规范的写法,JSON对应的正确MIME类型为application/json,且Accept头通常会携带多类型声明、权重参数,直接全等判断会漏掉大量合法请求,建议用includes做包含匹配。
方案1:全局拦截器统一处理(推荐)
该方案适合大多数接口都需要兼容两种返回格式的场景,完全消除每个接口的重复判断逻辑,同时不需要手动注入@Res()对象,不会破坏Nest原生的响应流处理能力,原有的异常过滤器、全局响应转换等功能都可以正常使用。
- 先创建拦截器:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { Response } from 'express'; @Injectable() export class DynamicResponseInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const httpCtx = context.switchToHttp(); const request = httpCtx.getRequest(); const response = httpCtx.getResponse<Response>(); const acceptHeader = request.headers.accept || ''; return next.handle().pipe( map(data => { // 匹配HTML请求走模板渲染 if (acceptHeader.includes('text/html')) { // 约定控制器返回数据中view字段为模板路径,其余字段为模板入参 const { view, ...templateParams } = data; if (view) { return response.render(view, templateParams); } } // 默认返回JSON格式 return data; }) ) } }
- 全局注册拦截器,在
main.ts中添加:
import { DynamicResponseInterceptor } from './interceptors/dynamic-response.interceptor'; // 其他初始化逻辑... app.useGlobalInterceptors(new DynamicResponseInterceptor());
- 控制器代码简化为:
@Get() async getAll() { const products = await this.productsService.getAll(); return { view: this.appService.getViewName(), products }; }
方案2:自定义装饰器按需使用
该方案适合仅少数接口需要兼容两种返回格式的场景,不需要全局注册,侵入性更低。
- 创建自定义装饰器:
import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import { Response } from 'express'; export const Renderable = createParamDecorator( (defaultView: string, ctx: ExecutionContext) => { const httpCtx = ctx.switchToHttp(); const request = httpCtx.getRequest(); const response = httpCtx.getResponse<Response>(); const acceptHeader = request.headers.accept || ''; return (data: any, customView?: string) => { if (acceptHeader.includes('application/json')) { return data; } const view = customView || defaultView; return response.render(view, data); } } )
- 控制器使用:
@Get() async getAll(@Renderable() render) { const products = await this.productsService.getAll(); return render({ products }, this.appService.getViewName()); }
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

