简体   繁体   中英

Spring REST get controller + method from route?

Is there a built in Spring way to say give me the RestController + method for "/customers/a" (/customers/{id})? Ie do the standard spring routing, but know which class/method is going to be called?

I'm trying to, in a central location, figure out based on the route if that method has a specific annotation. For centralized request / response logging.

I can iterate through the app for rest controllers and build the map myself, but wondering if there is anything exposed already to know where a request would go to?

Thanks.

You can use the Spring AOP module. And i think that a clean way (managing response without break the RestController endpoints flow) can be the following.

First of all add the spring-boot-starter-aop dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Let's see the code.

This is our ExampleRequest

import lombok.Data;

@Data
public class ExampleRequest {
    String name;
}

We assume that we want to validate our request -> if name == "anonymous" we want to return an HTTP status BAD_REQUEST.


This is our TestController

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
    @Anonymous
    @GetMapping("")
    public ResponseEntity<String> test(ExampleRequest exampleRequest, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        else return new ResponseEntity<>(HttpStatus.OK);
    }    
}

We pass as arguments the ExampleRequest that is our request and BindingResult that we will use later. Anyway if bindingResult has errors our validation logic is broken so we need to return the error we want!

The @Anonymous is our custom annotation that we will use to tell to our Advice(s) which methods, or better - which classes (an Aspect is an entire class "proxied"), we want to proxy

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anonymous {

}  

And our TestAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

@Aspect
@Component
public class TestAspect {

    @Before(value = "@annotation(Anonymous) && args(exampleRequest, bindingResult,..)", argNames = "joinPoint,exampleRequest,bindingResult")
    public Object logExecutionTime(JoinPoint joinPoint, ExampleRequest exampleRequest, BindingResult bindingResult) throws Throwable {
        if (exampleRequest.getName().equals("anonymous")) bindingResult.rejectValue("name", "incorrect");
        return joinPoint;
    }

}   

where we are saying: BEFORE the execution of EVERY METHODS annotated with our custom ANONYMOUS annotation with these ARGUMENTS execute this logic!

Obviously you will have your customized validation logic so implement it.


As I was saying in comments above i think you could return errors directly from the proxied class but at the same time I think that this example is a cleaner way to work with AOP (using BindingResult the flow of endpoints is respected and this technique does't break other validations of your model classes. You can use tha javax validation for example mixing in the advices and all will work perfectly!), personally I love this Spring AOP module but don't abuse it !

For reference: https://docs.spring.io/spring/docs/2.5.x/reference/aop.html

You might want to try an another approach:

Use Aspect Oriented programming supported by spring. Create an aspect that will act as an interceptor for all your controllers. The Advice will be called right before (or "instead of", depending on the type of advice) the actual rest method so that you'll be able to get the access and then will forward the execution to the real controller. In the code of aspect you could introspect the method to be called on a real controller and figure out whether it has annotations.

I've found an example of doing exactly this Here (You'll have to slightly change the point-cut definition to make the aspect applicable to the rest controller of your choice).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM