Spring HandlerInterceptor: how to access class annotations? - java

I registered my interceptor with the following code
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( myInterceptor() );
}
...
}
Here the interceptor definition
public class MyInterceptorimplements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Check to see if the handling controller is annotated
for (Annotation annotation : Arrays.asList(handler.getClass().getDeclaredAnnotations())){
if (annotation instanceof MyAnnotation){
... do something
However the handler.getClass().getDeclaredAnnotations() is not returning the class level annotations of the Controller intercepted.
I can only get the method level annotations which is not what I want in this case.
The same interceptor works fine with xml configuration (using Spring 3):
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="myInterceptor"/>
</list>
</property>
</bean>
Is there a way to have class level information in Spring 4?
According to
In a Spring-mvc interceptor, how can I access to the handler controller method?
"HandlerInterceptors will only provide you access to the HandlerMethod" using the configuration above. But what is the alternative configuration to get class level information?

You can access spring controller class level annotations in the interceptor using handler method.
But you have to be aware that the Casting to HandlerMethod might throw an exception because no Method was found (404)
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre-handle");
HandlerMethod hm;
try {
hm = (HandlerMethod) handler;
} catch (ClassCastException e) {
return super.preHandle(request, response, handler);
}
Method method = hm.getMethod();
// Sometimes Controller.class wont work and you have to use RestController.class just depends on what you use.
if (method.getDeclaringClass().isAnnotationPresent(Controller.class)) {
if (method.isAnnotationPresent(ApplicationAudit.class)) {
System.out.println(method.getAnnotation(ApplicationAudit.class).value());
request.setAttribute("STARTTIME", System.currentTimemillis());
}
}
return true;
}

Related

springboot java interceptor doesn't invoke TRACE commad

I define an interceptor in spring-boot.
I override the preHandle method.
the interceptor is invoking for all HTTP commands : GET/PUT/POST/PATCH/DELETE/HEAD/OPTIONS
but it doesn't invoked for TRACE command.
what am I miss?
the interceptor:
#Component
public class BlockingHttpInterceptor implements HandlerInterceptor {
private final Class<?> thisClass = this.getClass();
private String BASE_URL = "/subscribers";
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (HttpMethod.GET.matches(request.getMethod())
|| HttpMethod.POST.matches(request.getMethod())
|| (HttpMethod.DELETE.matches(request.getMethod()) && request.getRequestURI().startsWith(BASE_URL))
|| HttpMethod.PATCH.matches(request.getMethod())) {
return true;
} else {
response.sendError(HttpStatus.METHOD_NOT_ALLOWED.value());
return false;
}
}
}
the interceptor config:
#Configuration
public class InterceptorConfig implements WebMvcConfigurer {
#Autowired
private BlockingHttpInterceptor blockingHttpInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(blockingHttpInterceptor).addPathPatterns("/**");
}
}
As explained in the JavaDoc for the DispatcherServlet the TRACE request are by default not dispatched, hence they will never reach your controllers/interceptor.
Luckily you are using Spring Boot which makes configuring this quite easy through the spring.mvc.dispatch-trace-request property, which is by default false. Setting this to true in your application.properties will enable dispatching for TRACE request.
spring.mvc.dispatch-trace-request=true
Adding the above to your properties will enable it and will make things work as you expect.

Spring - Execute code before controller's method is invoked

Is there any annotation similar to #PreAuthorize or #PreFilter that I can use to run code before a method in the Controller is invoked?
I need to add info to the request context (specific to the method being called) to be then retrieved by the ExceptionHandler.
For example
#RestController
public MyController{
#UnkwonwAnnotation("prepareContext(request.getAgentId())"){
public ResponseEntity method1(RequestA requestA) {
...
}
#UnkwonwAnnotation("prepareContext(request.getUserName())"){
public ResponseEntity method1(RequestB requestB) {
...
}
}
I could actually just use #PreAuthorize but doesn't feel right
You Can add interceptor for this
Sample Interceptor
public class CustomInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response) {
//Add Login here
return true;
}
}
Configuration
#Configuration
public class MyConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
}
}
Hope this helps
Spring Aspect is also a good option to execute code before controller.
#Component
#Aspect
public class TestAspect {
#Before("execution(* com.test.myMethod(..)))")
public void doSomethingBefore(JoinPoint jp) throws Exception {
//code
}
}
Here myMethod() will execute before controller.
Maybe a good option is implement a custom filter that runs every time that a request is received.
You need extend "OncePerRequestFilter" and overwrite the method "doFilterInternal"
public class CustomFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//Add attributes to request
request.getSession().setAttribute("attrName", new String("myValue"));
// Run the method requested by petition
filterChain.doFilter(request, response);
//Do something after method runs if you need.
}
}
After you have to register the filter in Spring with FilterRegistrationBean. If you have Spring security yo need add your filter after security filter.
Expanding on Sai Prateek answer, I'v created a custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface OperationContext {
String clientId();
String userId();
String operation();
}
and a component to handle it:
#Aspect
#Component
public class OperationContextAspect {
#Before(value = "#annotation(operationContext)", argNames = "operationContext")
public void preHandle(OperationContext operationContext) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
requestAttributes.setAttribute("operation", operationContext.operation, RequestAttributes.SCOPE_REQUEST);
requestAttributes.setAttribute("clientId", operationContext.clientId(), RequestAttributes.SCOPE_REQUEST);
requestAttributes.setAttribute("userId", operationContext.userId(), RequestAttributes.SCOPE_REQUEST);
}
}
I then annotate the controller methods providing the required parameters:
#RestController
public class MyController {
#OperationContext(clientId = '#request.getClientId', userId = '#request.getUserId', operation = "OPERATION_A")
public ResponseEntity aMethod(MyRequest request) {
...
}
}

How to run some code before executing Spring web service

I implemented some rest web-services using spring.
I have some common code that I need to perform before every web service.
Crrently i explicitly call this common code at the start of each web service.
Is there a way to have spring "automatically" call this common code before calling web services?
You should using the spring aop intercept every web services,and execute the common code on it.Like following code:
<bean id="aspect" class="com.zhuyiren.Aspect"/>
<aop:config>
<aop:aspect ref="aspect">
<aop:before method="before" pointcut="execution(* com.zhuyiren.service..*.*(..))"/>
</aop:aspect>
</aop:config>
That above code means the invoke every method of some bean packaged at com.zhuyiren.service always execute the method common in com.zhuyiren.Aspect.You can write the common code in common method.Like:
public void common(){
System.out.println("execute some common code");
}
The test case:
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
UserService service = context.getBean(UserService.class);
System.out.println(service.getUser(3));
}
The print is :
execute some common code
UserInfo{id=3, name='zhuyiren', password='123456', testString=[]}
The answer depends on the code that you want to execute.
One technique is to use AOP (as described in the #dabaicai answer).
A different technique is to add a JEE filter to process the request before it lands in your handler method.
Here are some links:
JEE6 Tutorial Filtering Requests and Responses
Oracle's Essentials of filters
Mkyong's Spring JEE filters - Take this with a grain of salt, Mkyong is light on explanation.
Another option: google search for "spring jee filter"
Spring have notion of interceptors - piece of code which "wraps" incoming requests
The simplest is to implement HandlerInterceptor interface:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
public class LetterzInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration<String> headerNames = request.getHeaderNames();
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
Next step is to register the interceptor with Spring:
#Configuration
public class MyConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}

Audit/Log all incoming web requests to #RequestMapping annotated methods

Source
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping
I have Controller classes annotated with #Controller, they have methods annotated with #RequestMapping. My task is to audit all the web requests received by the Controller classes to #RequestMapping methods, I am using datatables on UI to send and receive response from controllers. the Audit framework is already in place.
The project is configured in Java Config.
I am not sure how to proceed on getting this done.
// Configure Interceptor
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
public #Bean
RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setAlwaysUseFullPath(true);
handlerMapping.setUseSuffixPatternMatch(false);
return handlerMapping;
}
}
//Add Handler
#Component
public class MyInterceptor extends HandlerInterceptorAdapter {
#Inject RequestMappingHandlerMapping requestMappingHandlerMapping;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// do stuff - ( Does my Audit Code go here? eg: Logger.info("xyz request"))
return true;
}
}
I was thinking something like this would work.
Any Suggestions on this, and
if its easier Using Listener or some other Interceptor, it would be helpful
Using interceptors you have full access to the HandlerMethod which provides convenient access to method parameters, the method return value, method annotations, etc.
The following example intercepts and logs mapped requests.
class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorAdapter() {
Logger logger = LoggerFactory.getLogger(WebMvcConfig.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
logger.info("{} - {} - method '{}' on controller '{}'",
request.getMethod(), request.getRequestURI(), method.getName(),
handlerMethod.getBean().getClass()
);
}
return true;
}
});
}
}
It returns true to continue with the execution chain (and forward the request to other interceptors or the controller-method itself).
An example log-output looks like:
GET - /greeting - method 'greeting' on controller 'class hello.GreetingController'

How to set Controller-wide global variables

Currently I use #ModelAttribute to set global variables (e.g. tweets for the footer) in my #Controller:
public #Controller class MainController {
public #ModelAttribute void global(ModelMap map) {
map.addAttribute("tweets", /*...*/null);
}
}
But they're logically gone when creating another Controller to keep things clean and separated:
public #Controller class GalleryController {
// ...
}
What's the best practice to set global variables Controller wide?
If you want to put some data to every page it is easy to use interceptor:
public class PagePopulationInterceptor extends HandlerInterceptorAdapter {
#Autowired
private UserService userService;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(modelAndView != null) {
User user = userService.findOne(request);
modelAndView.addObject("myUserProfile", user);
}
}
}
To make this work also declare interceptor in webmvc-config.xml (spring configuration for webapp):
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.yourcompany.yourapp.util.PagePopulationInterceptor" />
</mvc:interceptor>
<!-- other interceptors (locale, theme and so on) -->
</mvc:interceptors>
you can extend HandlerInterceptorAdapter and add common modelAttributes thr

Categories