Spring MVC: how to get a handler method for a request - java

I am trying to implement some logic depending on annotation present on method with Spring #RequestMapping annotation.
So I have a HttpServletRequest instance in my method and I want to ask spring "give me a method, which will be invoked to handle this request", so I can use reflection API to ask if my annotation is present or not, so I can alter the processing.
Is there any easy way to get this information from Spring MVC?

I suppose you have a handler method like:
#SomeAnnotation
#RequestMapping(...)
public Something doHandle(...) { ... }
And you want to add some pre-processing logic for all handler methods that are annotated with #SomeAnnotation. Instead of your proposed approach, you can implement the HandlerInterceptor and put your pre-processing logic into the preHandle method:
public class SomeLogicInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
SomeAnnotation someAnnotation = handlerMethod.getMethodAnnotation(SomeAnnotation.class);
if (someAnnotation != null) {
// Put your logic here
}
}
return true; // return false if you want to abort the execution chain
}
}
Also don't forget to register your interceptor in your web configuration:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SomeLogicInterceptor());
}
}

Helped by #ali-dehgani's answer, I have a more flexible implementation that doesn't need to register an interceptor. You do need to pass the request object that is bound to be mapped to that method.
private boolean isHandlerMethodAnnotated(HttpServletRequest request ) {
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
Map<String, HandlerMapping> handlerMappingMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(webApplicationContext, HandlerMapping.class, true, false);
try {
HandlerMapping handlerMapping = handlerMappingMap.get(RequestMappingHandlerMapping.class.getName());
HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
Object handler = handlerExecutionChain.getHandler();
if(handler instanceof HandlerMethod){
Annotation methodAnnotation = ((HandlerMethod) handler).getMethodAnnotation(MyAnnotation.class);
return methodAnnotation!=null;
}
} catch (Exception e) {
logger.warn(e);
}
return false;
}

Related

Invoke Same Method for Multiple Services in Application

My application consists of multiple services.We had a requirement now that for every request coming in to our application we need to validate the token.
Current architecture of my application is such that every microservice has its own ServiceInterceptor class and in that class I am writing the logic in prehandle method to validate token recieved in request.
Service Interceptor Class.
#Component
public class ServiceInterceptor implements HandlerInterceptor {
private static final ApplicationLogger logger = ApplicationLogger.getInstance();
#Autowired
TokenInfoServiceImpl tokenInfoServiceImpl;
#Override
#CrossOrigin(origins = "*", maxAge = 3600)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestPath = request.getRequestURL().toString();
String authToken = request.getHeader("authToken");
String bearerToken = request.getHeader("Authorization");
String userId = request.getHeader("userId");
if (deviceId.equals("web")) {
if (bearerToken.startsWith("Bearer ")){
bearerToken = bearerToken.substring(7, bearerToken.length());
} else {
response.sendError(400, "Expected bearer prefix to Authorization header value.");
}
boolean isTokenValid = tokenInfoServiceImpl.validateToken(bearerToken);
return isTokenValid;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
My concern is since we have different services,every service has its interceptor class , so in every service will I have to create method validateToken(to validate the token when a request comes to that service) which is obviously not at all an ideal approach.
Is there a way that I could write validateToken method in one place and that could be accessed by all the services(like UserService,PaymentService,etc..) or rather one Interceptor could be used to intercept request for all the individual microservices instead of having separate interceptor for each service .
I know this can be done using API Gateway but right now our team want a quick solution to this .API Gateway will implement later.
If I understand your question & comments you can try below :
Create Configuration bean which implements WebMvcConfigurer
Use your ServiceInterceptor inside addInteceptor & mention endpoints or root context if all endpoints needed this config :
#Configuration
public class ConfigClass implements WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ServiceInterceptor ()).addPathPatterns("/contextroot/**");
}
}
Also you may directly use your ServiceInterceptor without annotating it with Component.

How to trigger a callback post fetching an entity in spring boot

#Service
#GetMapping
public Foo findByFooId(#RequestParam(name = "fid") String fooId) {
return fooService.findByFooId(fooId);
}
I would like to trigger and save who viewed Foo, using a different method in FooService.
Its like a PostConstruct callback for a successful response of findByFooId. How can this be achieved
One way is going to a custom HandlerInterceptor implementation.
Definition of the interceptor
public class FooViewerInterceptor extends HandlerInterceptorAdapter {
#Autowired
FooService fooService;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
// if response succeeded ? http response code = 200 ?
// extract the "who" logic
// extract the fooId from request path
fooService.viewedBy(fooId, userId); // example...
}
}
Register the interceptor. Note the path pattern specified with the custom interceptor instance.. just an example.
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FooViewerInterceptor()).addPathPatterns("/foo/**");
}
}

Validation of JWT token along with requestBody parameter in springBoot

I have an reactive spring boot application where I am having different controllers and all the controller having get, post, put, delete methods
GET and DELETE method URI format => /{userName}/{others}
and it's ensured that put and post method must have a field userid in their request body.
Also All the request having an authorization header.
And I already have a method called validate that accepts 2 parameters authorizationHeader and userName and returns true if this mapping exists false if not.
I am trying to write generic filter can filter incoming request and validate before going to controller.
How can I write this generic webfilter especially how to extract body from post request and validate requests.
I tried writing this
#Component
#Slf4j
public class ExampleWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
ObjectMapper mapper = new ObjectMapper();
return serverWebExchange
.getRequest()
.getBody()
.next()
.flatMap(body -> {
try {
return validate(body, serverWebExchange
.geHeaders().get(0))
} catch (IOException e) {
return Mono.error(e);
}
})
.flatMap((boolean s) -> {
return webFilterChain.filter(serverWebExchange);
});
}
Mono<Boolean> validate(DataBuffer body, String Header){
//my logic to validate
}
}
But it seems it's hanging after this filter method executed. so my question is
How can I write webfilter which will read body and validate?
Is there any other generic solution available for this type of problem in spring-boot?
I think you should use Interceptors. You can intercept the http call, and make your validations on the request. You can do this as global or you can do this for specific endpoints/paths. Here is a example for your case.
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//make validations
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {
//make validations
}
}
After this you need to register your interceptor like below.
#Component
public class ProductServiceInterceptorAppConfig extends WebMvcConfigurerAdapter {
#Autowired
ProductServiceInterceptor productServiceInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(productServiceInterceptor);
}
}
For more depth information you can visit the links below.
https://www.youtube.com/watch?v=agBadIAx0Wc
https://www.tutorialspoint.com/spring_boot/spring_boot_interceptor.htm

Custom annotation filter Spring boot

I see that with Spring boot is really simple create filters. Just follow post like this one https://www.baeldung.com/spring-boot-add-filter
What I have not able to find, is how to create annotations that subscribe specifics endpoints in the controller to one filter.
Something like in Jax-RS it would looks like
#GET
#Path("jax-rs-single")
#Reactive(ttlRequest = 2000)
#Produces(MediaType.APPLICATION_JSON)
public Single getSingle() {
return Single.just("Hello world single");
}
Where #Reactive it would trigger the ReactiveFilter implementation per request.
I also saw the #WebFlow annotation, but it's not what I want. I want to create a library where the consumers decide which filter use, just adding the annotation in the controller.
Any idea how to do something similar with Spring boot/MVC ?
Regards
I will try to describe here more about Custom annotation and the processor in Spring.
I don't know what you want or what you need, but I will give an generic example.
You have 2 options:
BeanProcessor
HandlerInterceptor
BeanProcessor
You need to build 3 things basically: Annotaton, BeanProcessor and a Callback to execute your logic if annotated. Here is an example of it and how it works:
1 - Create the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Documented
public #interface Reactive {
Integer ttlRequest;
}
2 - Implement a BeanPostProcessor
#Component
public class ReactiveAnnotationProcessor implements BeanPostProcessor {
private ConfigurableListableBeanFactory configurableBeanFactory;
#Autowired
public ReactiveAnnotationProcessor(ConfigurableListableBeanFactory beanFactory) {
this.configurableBeanFactory = beanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
this.scanReactiveAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
protected void scanReactiveAnnotation(Object bean, String beanName) {
this.configureMethodInjection(bean);
}
private void configureMethodInjection(Object bean) {
Class<?> managedBeanClass = bean.getClass();
MethodCallback methodCallback =
new ReactiveMethodCallback(configurableBeanFactory, bean);
ReflectionUtils.doWithMethod(managedBeanClass, methodCallback);
}
}
3 - Create the method callback (here is the logic to execute)
public ReactiveMethodCallback implements MethodCallback {
private ConfigurableListableBeanFactory configurableBeanFactory;
private Object bean;
public ReactiveMethodCallback(ConfigurableListableBeanFactory bf, Object bean) {
configurableBeanFactory = bf;
this.bean = bean;
}
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (!method.isAnnotationPresent(Reactive.class)){
return;
}
//YOUR LOGIC HERE
}
}
Here is a good source about annotation processing, it is about FieldProcessing but you can just change the interfaces to implement what you need if you have doubts: https://www.baeldung.com/spring-annotation-bean-pre-processor
[UPDATED] You can also create a HandlerInterceptor instead:
HandlerInterceptor
public class ReactiveFilterHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Test if the controller-method is annotated with #CustomFilter
Reactive filter = handlerMethod.getMethod().getAnnotation(Reactive.class);
if (filter != null) {
// ... do the filtering, or call the Component for filtering
}
}
return true;
}
}
And register your handler:
#Configuration
public class WebMvcConfig extends WebMvcConfigurer {
#Autowired
ReactiveFilterHandlerInterceptor reactiveFilterHandlerInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(reactiveFilterHandlerInterceptor);
}
}
If I understand what you want correctly the main problem is how to apply filter based on custom annotation.
So first of all, yes you can use a regular Spring filter (WebFilter in case of Spring Webflux or Filter in case of Spring MVC), but you'll need to write some custom logic.
To do filtering based on annotation you should:
Use RequestMappingHandlerMapping#getHandlerInternal() method to retrieve a reference to the method that handles the request (the getSingle() in your case)
When you manage to retrieve the HandlerMethod then you can check if that method has your custom annotation applied with hasMethodAnnotation(Class<A> annotationType) method.
When you know that, then you can react accordingly: either chain.doFilter(request, response) without performing any actions, or apply your custom logic, and then trigger the rest of the filter chain.

Java interceptor not getting called

I have a Spring boot application and I am implementing and interceptor in order to log some data.
The problem is that is not getting called, I have tried:
#Interceptor
public class LoggerInterceptor{
#AroundInvoke
public Object collectBasicLoggingInformation(InvocationContext context) throws Exception {
Logger logger = LoggerFactory.getLogger(context.getClass());
logger.info("Method Called: " + context.getMethod()
.getName());
logger.info("Parameters: " + Arrays.toString(context.getParameters()));
return context.proceed();
}
}
And then I've applied to methods or classes and in both of them doesn't work:
#GetMapping
#Interceptors(LoggerInterceptor.class)
public List getAllFilingNumber(){
logger.info("This is a test");
return filingNumberService.findAll();
}
Or
#RestController
#RequestMapping(FilingNumberController.BASE_URL)
#Interceptors(LoggerInterceptor.class)
public class FilingNumberController{
#GetMapping
public List getAllFilingNumber(){
logger.info("This is a test");
return filingNumberService.findAll();
}
}
Does someone knows what I am doing wrong?
Thanks
If you are having a springboot application in order to intercept the request to a controller , you have to take a different approach altogethor.
Interceptors are used in conjunction with Java EE managed classes to
allow developers to invoke interceptor methods on an associated target
class, in conjunction with method invocations or lifecycle events.
Common uses of interceptors are logging, auditing, and profiling.
Reference Doc
You are trying to use Java EE annotation with spring , which won't work.In spring-boot you will have to register the interceptors like :
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
}
}
The interceptor itself have to be a class which extends the HandlerInterceptorAdapter and implements the methods as follows.
From Spring DOCS :
All HandlerMapping implementations support handler interceptors that
are useful when you want to apply specific functionality to certain
requests — for example, checking for a principal. Interceptors must
implement HandlerInterceptor from the org.springframework.web.servlet
package with three methods that should provide enough flexibility to
do all kinds of pre-processing and post-processing:
preHandle(..): Before the actual handler is executed
postHandle(..): After the handler is executed
afterCompletion(..): After the complete request has finished
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
System.out.println("we are Intercepting the Request");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object object, ModelAndView model)
throws Exception {
System.out.println("request processing "
+ "completed by #RestController");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object object, Exception arg3)
throws Exception {
System.out.println("afterCompletion Request Completed");
}
}

Categories

Resources