You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS如何根据Accept请求头动态返回JSON或HBS渲染结果

实现方案

首先先修正现有代码的一个错误:你代码中判断headers.accept === "accept/json"是不符合规范的写法,JSON对应的正确MIME类型为application/json,且Accept头通常会携带多类型声明、权重参数,直接全等判断会漏掉大量合法请求,建议用includes做包含匹配。

方案1:全局拦截器统一处理(推荐)

该方案适合大多数接口都需要兼容两种返回格式的场景,完全消除每个接口的重复判断逻辑,同时不需要手动注入@Res()对象,不会破坏Nest原生的响应流处理能力,原有的异常过滤器、全局响应转换等功能都可以正常使用。

  1. 先创建拦截器:
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;
      })
    )
  }
}
  1. 全局注册拦截器,在main.ts中添加:
import { DynamicResponseInterceptor } from './interceptors/dynamic-response.interceptor';

// 其他初始化逻辑...
app.useGlobalInterceptors(new DynamicResponseInterceptor());
  1. 控制器代码简化为:
@Get()
async getAll() {
  const products = await this.productsService.getAll();
  return {
    view: this.appService.getViewName(),
    products
  };
}

方案2:自定义装饰器按需使用

该方案适合仅少数接口需要兼容两种返回格式的场景,不需要全局注册,侵入性更低。

  1. 创建自定义装饰器:
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);
    }
  }
)
  1. 控制器使用:
@Get()
async getAll(@Renderable() render) {
  const products = await this.productsService.getAll();
  return render({ products }, this.appService.getViewName());
}

内容的提问来源于stack exchange,提问作者nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 08:45:05