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

如何为Grails所有控制器的所有操作添加自定义响应头

问题描述

环境

  • Grails: 5.3.3(或Grails 6.x)
  • Java: OpenJDK 11

需求

为所有控制器的所有操作添加自定义响应头MyRespHeader。

尝试实现

编写了CorsInterceptor拦截器,代码如下:

class CorsInterceptor {
      CorsInterceptor() {
            matchAll()
            log.info("CorsInterceptor(): BEGIN ")

      }
      boolean before() {
            log.info("before(): BEGIN")
            true 
      }

      boolean after() {
            response.addHeader("MyRespHeader","My dream data here")
            log.info("after(): final")
            true
      }

      void afterView() {
            log.info("afterView(): BEGIN ...")
            response.setHeader("MyRespHeader","My dream data here")
        // no-op
      }

}

日志输出

2024-04-07 19:55:04.287  INFO --- [tp1902630939-52] com.example.CorsInterceptor              : before(): BEGIN
2024-04-07 19:55:04.312  INFO --- [tp1902630939-52] com.example.HomeController               : json:{
   "fieldname": "city",
   "value": "Tokyo city"
}
2024-04-07 19:55:04.319  INFO --- [tp1902630939-52] com.example.CorsInterceptor              : after(): final
2024-04-07 19:55:04.319  INFO --- [tp1902630939-52] com.example.CorsInterceptor              : afterView(): BEGIN ...

问题

日志显示CorsInterceptor的after()方法已执行,但Postman中无法找到自定义响应头MyRespHeader。已知在每个控制器的每个操作中直接添加response.addHeader("MyRespHeader","My dream data here")可生效,但这种方案不够优雅。


解决方案

方案1:使用Grails ResponseFilter(推荐)

拦截器的after()方法可能因控制器提前提交响应(比如直接返回JSON)导致无法修改响应头,使用ResponseFilter能更可靠地全局添加响应头:

  1. 创建CustomResponseHeaderFilter类:
import grails.web.mvc.ResponseFilter
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class CustomResponseHeaderFilter implements ResponseFilter {
    @Override
    void doFilter(HttpServletRequest request, HttpServletResponse response, Closure next) {
        next.call()
        response.addHeader("MyRespHeader", "My dream data here")
    }
}
  1. 在grails-app/conf/spring/resources.groovy中注册过滤器:
beans = {
    customResponseHeaderFilter(CustomResponseHeaderFilter)
}

方案2:调整拦截器的afterView()逻辑

如果坚持用拦截器,可修改afterView()方法——该方法在视图渲染完成后执行,此时响应通常未提交:

void afterView() {
    if (!response.isCommitted()) {
        response.addHeader("MyRespHeader", "My dream data here")
        log.info("Custom header added in afterView()")
    }
}

注意:若控制器直接返回JSON(如render json:),Grails可能提前提交响应,导致afterView()不执行,此时方案1更可靠。

方案3:Grails 6+ 用@ControllerAdvice + ResponseBodyAdvice

Grails 6.x基于Spring Boot 3.x,可通过Spring的全局响应处理机制实现:

import org.springframework.core.MethodParameter
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice

@ControllerAdvice
class CustomResponseHeaderAdvice implements ResponseBodyAdvice<Object> {
    @Override
    boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true
    }

    @Override
    Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                          Class<? extends HttpMessageConverter<?>> selectedConverterType,
                          ServerHttpRequest request, ServerHttpResponse response) {
        response.getHeaders().add("MyRespHeader", "My dream data here")
        return body
    }
}

该方式能覆盖所有控制器响应,包括直接返回JSON的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 15:32:43