You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring/Spring Boot中如何通过AOP更新请求头参数并传递给控制器(仅改首参)

解决Spring Boot AOP修改请求参数/头并传递给控制器的问题

一、为什么@Before设置request.setAttribute控制器拿不到值?

你之前用request.setAttribute("traceId", ...)后控制器获取到null,核心原因是控制器方法的参数默认从请求参数(@RequestParam)、路径变量(@PathVariable)或者请求头(@RequestHeader)中取值,而request.setAttribute()是把值存在请求的属性域里,要获取这个值需要在控制器参数上标注@RequestAttribute("traceId"),比如:

@GetMapping("/test")
public String test(@RequestAttribute("traceId") String traceId) {
    // 这里就能拿到设置的traceId了
}

但如果你的需求是让控制器直接通过普通参数(比如String traceId)拿到值,这种方式就不适用,需要换一种思路修改方法参数或者请求头。

二、用@Around仅修改第一个参数(traceId),其余参数保持不变

你之前的@Around写法需要手动传入所有参数,确实不够灵活。我们可以直接获取方法的参数数组,修改第一个元素后再执行方法,这样不管控制器方法有多少参数,都能只更新traceId:

@Around("execution(* com.test.api.*.*(..))")
public Object setTraceId(ProceedingJoinPoint joinPoint) throws Throwable {
    // 获取原方法的参数数组
    Object[] args = joinPoint.getArgs();
    
    // 检查参数数组不为空,且第一个参数是String类型(符合你说的所有方法第一个参数都是traceId的情况)
    if (args != null && args.length > 0 && args[0] instanceof String) {
        String newTraceId = ServiceUtil.getTraceId();
        // 修改第一个参数为新的traceId
        args[0] = newTraceId;
    }
    
    // 用修改后的参数执行原方法
    return joinPoint.proceed(args);
}

这个写法的优势很明显:

  • 不需要硬编码参数数量,自动适配所有控制器方法
  • 只修改第一个参数,其余参数完全保留原有值
  • 不需要在切点表达式里绑定args(traceId,..),代码更简洁灵活

三、如果需求是修改请求头(而非方法参数)

如果你是想让控制器通过@RequestHeader("traceId")拿到值,那原生的HttpServletRequest的header是不可修改的,需要用HttpServletRequestWrapper包装请求,替换请求上下文里的request:

@Around("PointcutDefinition.controllerLayer()")
public Object modifyRequestHeader(ProceedingJoinPoint joinPoint) throws Throwable {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (attributes != null) {
        HttpServletRequest originalRequest = attributes.getRequest();
        
        // 包装请求,重写header获取方法
        HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(originalRequest) {
            @Override
            public String getHeader(String name) {
                // 当获取traceId头时,返回新生成的ID
                if ("traceId".equals(name)) {
                    return ServiceUtil.getTraceId();
                }
                return super.getHeader(name);
            }

            @Override
            public Enumeration<String> getHeaders(String name) {
                if ("traceId".equals(name)) {
                    return Collections.enumeration(Collections.singletonList(ServiceUtil.getTraceId()));
                }
                return super.getHeaders(name);
            }
        };
        
        // 替换请求上下文里的request
        attributes.setRequest(wrappedRequest);
    }
    
    return joinPoint.proceed();
}

这样控制器里用@RequestHeader("traceId") String traceId就能拿到更新后的traceId了。


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

火山引擎 最新活动