如何为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能更可靠地全局添加响应头:
- 创建
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") } }
- 在
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
相关产品推荐
相关产品推荐

