如何在使用切面时向HttpServletRequest添加自定义标头?

huangapple go评论69阅读模式
英文:

How to add my custom header to a HttpServletRequest with aspect?

问题

我尝试创建一个切面以覆盖在控制器处理方法实际执行之前的HttpServletRequest参数。但事实证明,即使我在切面方法中用自定义的HttpServletRequestWrapper替换它,HttpServletRequest始终是一个RequestFacade实例。

这是代码:

// 注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
}

// 切面:
@Component
@Aspect
@Slf4j
public class MyAspect {
    @Pointcut("@annotation(com.example.springtest.annotation.MyAnnotation)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object setHeader(ProceedingJoinPoint pjp) throws Throwable {
        var args = pjp.getArgs();
        var signature = (MethodSignature) pjp.getSignature();
        var method = signature.getMethod();
        var annotations = method.getDeclaredAnnotations();
        var need = Arrays.stream(annotations).anyMatch(a -> a.annotationType().equals(MyAnnotation.class));
        if (!need) {
            return pjp.proceed();
        }

        for (var i = 0; i < args.length; i++) {
            var arg = args[i];
            if (arg instanceof HttpServletRequest request) {
                args[i] = new HttpServletRequestWrapper(request) {
                    @Override
                    public String getHeader(String name) {
                        if (name.equalsIgnoreCase("my-header")) {
                            return "my-header";
                        } else {
                            return super.getHeader(name);
                        }
                    }
                };
            }
        }
        return pjp.proceed();
    }

}

// 启动类:
@Configurable
@EnableAspectJAutoProxy
@SpringBootApplication
public class SpringtestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringtestApplication.class, args);
    }

}

// 控制器:
@RestController
public class TestController {

    private static final Logger log = LoggerFactory.getLogger(TestController.class);

    @MyAnnotation
    @GetMapping("/get-my-header")
    public String getMyHeader(HttpServletRequest request) {
        if (Objects.requireNonNull(request) instanceof HttpServletRequestWrapper) {
            log.info("is wrapper");
        } else {
            log.info("request:{}", request.getClass());
        }
        return request.getHeader("my-header");
    }
}

所以发生了什么?在我替换后,是否有任何其他代理类实例改变了我的HttpServletRequest参数?

日志显示切面和控制器中的请求参数都是RequestFacade实例。

我还尝试创建一个服务方法,直接将我的自定义HttpServletRequestWrapper实例发送给它。这次参数没有改变。

英文:

I tried to create an aspect to override the
HttpServletRequest
parameter before the controller handler method actually gets executed. But it turns out the HttpServletRequest is always a RequestFacade instance even though I replace it with a Custom HttpServletRequestWrapper in my aspect method.

Here is the code:

// the annotation:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
}
// the aspect:
@Component
@Aspect
@Slf4j
public class MyAspect {
@Pointcut(&quot;@annotation(com.example.springtest.annotation.MyAnnotation)&quot;)
public void pointcut() {
}
@Around(&quot;pointcut()&quot;)
public Object setHeader(ProceedingJoinPoint pjp) throws Throwable {
var args = pjp.getArgs();
var signature = (MethodSignature) pjp.getSignature();
var method = signature.getMethod();
var annotations = method.getDeclaredAnnotations();
var need = Arrays.stream(annotations).anyMatch(a -&gt; a.annotationType().equals(MyAnnotation.class));
if (!need) {
return pjp.proceed();
}
for (var i = 0; i &lt; args.length; i++) {
var arg = args[i];
if (arg instanceof HttpServletRequest request) {
args[i] = new HttpServletRequestWrapper(request) {
@Override
public String getHeader(String name) {
if (name.equalsIgnoreCase(&quot;my-header&quot;)) {
return &quot;my-header&quot;;
} else {
return super.getHeader(name);
}
}
};
}
}
return pjp.proceed();
}
}
// the bootstrap class:
@Configurable
@EnableAspectJAutoProxy
@SpringBootApplication
public class SpringtestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringtestApplication.class, args);
}
}
// and the controller
@RestController
public class TestController {
private static final Logger log = LoggerFactory.getLogger(TestController.class);
@MyAnnotation
@GetMapping(&quot;/get-my-header&quot;)
public String getMyHeader(HttpServletRequest request) {
if (Objects.requireNonNull(request) instanceof HttpServletRequestWrapper) {
log.info(&quot;is wrapper&quot;);
} else {
log.info(&quot;request:{}&quot;, request.getClass());
}
return request.getHeader(&quot;my-header&quot;);
}
}

So what happend there? Is there any other proxy class instance change my HttpServletRequest parameter after I replaced it?

the logs show that the request parameter in both the aspect and the controller is a RequestFacade instance.

I also tried to create a service method and directly send my custom HttpServletRequestWrapper instance to it. This time the parameter is not changed.

答案1

得分: 0

抱歉,代码部分不提供翻译。以下是翻译的文本内容:

"我担心修改请求标头的操作不是那么容易。我也担心这个方面不是一个可行的方式。

以下是一个用于Spring Boot 2的最小可复现示例,通过在自定义拦截器中设置一个命名的attribute到请求中,然后在@RestController的方法中捕获。

拦截器

@Component
public class CustomInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		request.setAttribute("fooAttribute", "fooValue");
		return true;
	}
}

配置

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

	@Autowired
	private CustomInterceptor customInterceptor;

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
			registry.addInterceptor(customInterceptor);
	}
}

控制器

我让这个属性在响应体中返回。

@GetMapping("hello")
public ResponseEntity<String> hello(@RequestAttribute String fooAttribute) {
	return ResponseEntity.ok(fooAttribute);
}

测试一下:

curl http://localhost:8080/hello
fooValue
```"
英文:

I am afraid fiddling with request headers' modification is not that easy. I am also afraid that aspect is not a way to go.

Here is a minimal and reproducible sample for Spring Boot 2 to achieve what you want by setting a named attribute to the request in a custom interceptor that is captured in a method of the @RestController.

Interceptor

@Component
public class CustomInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		request.setAttribute(&quot;fooAttribute&quot;, &quot;fooValue&quot;);
		return true;
	}
}

Configuration

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

	@Autowired
	private CustomInterceptor customInterceptor;

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
			registry.addInterceptor(customInterceptor);
	}
}

Controller

I let the attribute be returned in the body.

@GetMapping(&quot;hello&quot;)
public ResponseEntity&lt;String&gt; hello(@RequestAttribute String fooAttribute) {
	return ResponseEntity.ok(fooAttribute);
}

Test it out:

curl http://localhost:8080/hello

>
&gt; fooValue
&gt;

huangapple
  • 本文由 发表于 2023年7月20日 12:11:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76726624.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定