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'
Related
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.
I am using spring boot for creating microservices. I need to implement request scope beans as I get some information in header and need this to be available across all the classes for that particular request. Below is what I did, but I get null pointer error.
#Component
#RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public Class RequestHeaderInfo {
private String appInfo;
...
}
#Component
public class RequestFilter implements Filter {
#Autowired
private RequestHeaderInfo requestHeaderInfo;
public void doFilter(ServletRequest req,....) {
HTTPServletRequest request = (HTTPServletRequest) req;
requestHeaderInfo.setAppInfo(request.getHeader("appInfo")) //throws null pointer error here
....
}
}
#Contoller
public class RestController {
#Autowired
private RequestHeaderInfo requestHeaderInfo;
}
I want this request header info object to be available throughout the particular request. In my filter class it throws null pointer error. Am I on the right track implementing request scoped bean?
In our project (Spring boot multi-module and not microservices) we are using an Interceptor class to filter the request.
#Component
public class RequestInterceptor implements org.springframework.web.servlet.HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String appInfo = request.getHeader("appInfo");
}
#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 {
}
}
Plus our Bean with the request scope is configured like this
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class RequestBean {
}
Hope this helps.
I migrate the code to springboot and our API works well. Only interceptor can't be triggerred.
I googled related solutions and modify the code to right format which still failed to trigger the interceptor.
In our project, we also have the filter which extends OncePerRequestFilter and works.
It makes me confused.
They should be no big difference.
Btw, AOP is used in the project.
It's my code.
JerseyConfig.class
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.xxx");
}
}
VaultAuthorizationInterceptor.class
#Component
public class VaultAuthorizationInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(VaultAuthorizationInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("test");
return true;
}
}
VaultAuthConfig.class
#Configuration
public class VaultAuthConfig implements WebMvcConfigurer {
#Bean
public VaultAuthorizationInterceptor getVaultInterceptor() {
return new VaultAuthorizationInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getVaultInterceptor()).addPathPatterns("/**");
}
}
When you are using the spring-boot-starter-jersey, you use jersey as your web stack. That means any requests will processed by jersey. So you have to register a jersey filter or interceptor. Take a look at the jersey documantation. There is described how to use filters and interceptors. I think you want to use a filter because interceptors in the jersey stack used to manipulate the input or output stream.
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) {
...
}
}
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;
}