1

recently I was searching for how to change the headers and body of the HttpServletRequest object before starting the controller logic, the only way that I found of achieve this is with an aspect, all good with this solution but the problem is that I have a lot of controllers classes, I need to change the value of an specific header in these controllers, I have added all my classes in my aspect:

@Pointcut("execution(* com.mypackage.Controller1..*(..)) || " +
          "execution(* com.mypackage.Controller3.someMethod*(..))
           //A lot of methods and packages
private void anyMethodWithHeader(){

@Around("anyMethodWithHeader()")
    public ResponseEntity<Map<String, Object>> changeHeaderValue(ProceedingJoinPoint jp) throws Throwable{
        //Here I got the object array with the arguments that I need

So my question is: There is a way in the @PointCut to put a regex or conditional or something like that in just one line or two, to get a specific header?

I mean, I have a lot of controllers, and those controllers receive a lot of headers, but I just want to change the value of a header named test-token, all I want is to avoid the addition of one more line in my aspect every time that I add a new controller in my project.

I was trying adding the package of the @RequestHeader in my aspect and other things but nothing works.

Thanks for the comments.

snake_404
  • 111
  • 5
  • 15
  • What do your controller methods have in common which differentiates them from non-controllers? A certain base package? A naming pattern? A `@Controller` or `@RestController` annotation on the class? Certain parameter types in the target method signatures? If there is anything like that or you can refactor to achieve it (e.g. moving all controllers into subpackages named `*..controller`), I think I will be able to help you. – kriegaex Jan 18 '20 at 02:50
  • 1
    Why didn't you consider a HandlerInterceptor based solution ?https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html – R.G Jan 18 '20 at 08:12
  • @kriegaex I have all my classes under a certain package, they are `@RestController` and in many of those classes I have a required header in the method arguments, example: `@RequestHeader(value = "my-header") String myHeader` – snake_404 Jan 20 '20 at 16:43
  • @R.G I tried that solution but is the same problem that I have with a filter, you can get the body and headers of the request, but you can't change the content of the `HttpServletRequest` I tried with a custom object to handle only the headers (https://wilddiary.com/adding-custom-headers-java-httpservletrequest/) but with that solution you can add custom headers but I can't change the content of the existing headers, and I don't want to change the logic of all my classes. – snake_404 Jan 20 '20 at 16:47

2 Answers2

1

You commented:

@kriegaex I have all my classes under a certain package, they are @RestController and in many of those classes I have a required header in the method arguments, example:

@RequestHeader(value = "my-header") String myHeader

You get all @RestController classes like this:

@within(org.springframework.web.bind.annotation.RestController)

You get all classes in a certain package (and its subpackages) like this:

within(a.certain.package..*)

You can combine that into

@within(org.springframework.web.bind.annotation.RestController) &&
within(a.certain.package..*) &&
execution(* *(..))

The execution(* *(..)) part is not necessary in Spring AOP because it only supports method execution joinpoints. In AspectJ you need it because there you can also intercept method calls, constructors and other joinpoints.

If you can really rely 100% on a @RequestHeader parameter annotation in all target methods, you can use

execution(* *(.., @org.springframework.web.bind.annotation.RequestHeader (*), ..))

and also isolate the annotation and its value as I described here with request body (just change the annotation and also adjust the rest of the code to your needs).

If you could rely on the request header parameter to always be in the same relative position in the signature, e.g. first, second, third from left or right, it would be even easier because you could directly bind the method parameter to an advice method parameter via args(). Please let me know if that is the case and I can show you an example.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Hi, sorry for the delay, I guess the best solution for my particular case, is to divide the controllers in 2 different packages, one with the header that I need, and the other one where I don't have that header. I can't rely in the position of the argument because many members of the team are creating more controllers and we can make a mistake at the moment of coding. I will mark your comments as an answer and thank you very much for the help. – snake_404 Jan 30 '20 at 17:11
  • If someone can make a coding mistake, they can also put the controller into the wrong package. Besides, `execution(* *(.., @org.springframework.web.bind.annotation.RequestHeader (*), ..))` finds the parameter annotation in **any** position, you just need the solution I linked to in order to dynamically find out which one it is. IMO you should not try to accommodate all kinds of bad coding with you aspects but make clear coding guidelines and automatically enforce them. Also here AspectJ can help with `@DeclareWarning` and `@DeclareError` if you use the AspectJ compiler. – kriegaex Jan 31 '20 at 01:19
0

The pointcut could be set to a custom annotation that is used only on methods you want to be handled by the aspect. In addition to the fine control when the aspect is applied, the annotation could have parameters used by the aspect itself (effectively modifying its behavior).

To illustrate the idea, here is a small project of mine where this is used:


While aspects are powerful and very useful in many situations, in this particular case you could also use a filter.

MartinBG
  • 1,500
  • 13
  • 22
  • I tried with the filter before using the aspect, but you can't change the content of the HttpServletRequest, you can add custom headers and custom parameters. Or at least that was I found while I was searching for a solution. – snake_404 Jan 17 '20 at 23:10