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("@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();
}
}
// 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("/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");
}
}
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.
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("fooAttribute", "fooValue");
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("hello")
public ResponseEntity<String> hello(@RequestAttribute String fooAttribute) {
return ResponseEntity.ok(fooAttribute);
}
Test it out:
curl http://localhost:8080/hello
fooValue