MVC Java Config - HandlerInterceptor not excluding paths - java

I have a MVC Java configuration but the HandlerInterceptor is not excluding some patterns.
At the line marked with xxx, if
1) I add both addPatterns("/**") and excludePathPatterns("*.ecxld") to the HandlerInterceptor's InterceptorRegistration, the HandlerInterceptor.preHanlde() is NOT invoked at all. e.g .addPathPatterns("/**").excludePathPatterns("*.ecxld")
2) I add only excludePathPatterns("*.ecxld") to the HandlerInterceptor's InterceptorRegistration, the HandlerInterceptor.preHanlde() is still executed.
(the other interceptors are invoked fine).
Any pointers appreciated.
Thanks
#Configuration
public class MyMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(getInterceptorOne());
registry.addInterceptor(getMyHandlerInterceptor())
.excludePathPatterns("*.ecxld"); // **xxx**
registry.addInterceptor(getInterceptorTwo()
);
}

The patterns you specify for include and exclude are ant bases path expressions and not normal URL expressions as you would express in web.xml to map a servlet or filter for instance.
To make an exclude work you have to also include an include path (as you already noticed with your second remark). Next change your exclude pattern to /**/*.ecxld.
Your current expression *.ecxld would match file.ecxld but it will not match /file.ecxld or even /foo/file.ecxld. The /**/ part takes care of that. However to make it work it also requires an includePathExpression (the code checks if there is an includePathExpression when not it is ignoring the excludePathExpression).
So in short change your configuration to the following should solve your problem.
#Configuration
public class MyMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(getInterceptorOne());
registry.addInterceptor(getMyHandlerInterceptor())
.includePathPatterns("/**")
.excludePathPatterns("/**/*.ecxld");
registry.addInterceptor(getInterceptorTwo()
);
}

I know this was a long while ago but I just stumbled over the same problem. During my search I found the following blog. There it is mentioned that if the interceptors are configured as beans they will be automatically added to the chain.
I am now using Spring 4.1.x so there might be a difference but what solved it for me was the following:
(I tried to avoid defining them as a spring beans. It didn't help.)
I configured the interceptors as spring beans (so I could autowire stuff into them see here)
I changed my definition as follows:
registry.addInterceptor(getMyHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/login");
By putting the addPathPatterns before the excludePathPatterns the behavior of the interceptor suddenly worked fine.

After debugging, the interceptors are not executed in the order they were added. In the above example, interceptorOne, then interceptorTwo, then the handler (with the excluded pattern) was executed.

I run into this trouble, can't exclude the path.
After I debug, found out is because Spring security redirect to "/login", because of "/login" is included in "/**", that why cannot access.
Solution is add the login & logout link as exclude paths too!

I've faced a similar problem while working with SpringBoot.
How I solved this problem?
I made a method to return a new instance of the Interceptor. And you will have to write the excludePathPatters after the addPathPattern method of the registry.
Here's the code snippet:
#Bean
public AuthInterceptor getAuthInterceptor() {
return new AuthInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(**getAuthInterceptor()**)
.addPathPatterns("/**")
.excludePathPatterns("/login/**");
}
I hope this helps.

Related

Spring boot with Jetty - config servlet context path

I am using Spring Boot with Jetty.
I configure the context-path:
server.servlet.context-path=/test
When accessing http://localhost:8080/test, it doesn't work. But going to http://localhost:8080/test/ works.
Is /test and /test/ different? How can I access http://localhost:8080/test
I think you are looking for something similar to setUseTrailingSlashMatch
Docs:
Whether to match to URLs irrespective of the presence of a trailing slash. If enabled a method mapped to "/users" also matches to "/users/".
The default value is true.
Code:
public class Config extends WebMvcConfigurationSupport {
#Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false)
.setUseTrailingSlashMatch(false);
}
}

Spring MVC RequestMapping requires trailing slash

There seems to be weird behavior which I can't seem to pinpoint the reason for. When I access a particular url I get a 404 response while other urls that are handled by the same controller class works. I have to add a trailing / to the end of the url in order for the method to be called.
This method DOES NOT get called when accessing localhost:8080/newprofile
#RequestMapping(value="/newprofile", method=RequestMethod.GET)
public String newProfile(Model model, Principal principal) {
return "newprofile";
}
However, this one DOES get called when accessing localhost:8080/login
#GetMapping("/login")
public String login() {
return "login";
}
I have tried both GetMapping and RequestMapping but the methods are never called.
Both methods are contained in my controller class
#Controller
public class HomeResources {
//login
//new profile
}
There is a setting responsible for such behavior:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseTrailingSlashMatch-boolean-
Just turn it off:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseTrailingSlashMatch(false);
}
}
Note that the default behaviour of setUseTrailingSlashMatch changed from true to false since 6.0 in order to support the deprecation of the property.
If you want this to be enabled you have to set it to true now. But as it is marked deprecated, probably best not to do it and instead follow the advice in this Spring Boot 3.0.0 M4 Release Notes
Developers should instead configure explicit redirects/rewrites through a proxy, a Servlet/web filter, or even declare the additional route explicitly on the controller handler (like #GetMapping("/some/greeting", "/some/greeting/") for more targeted cases.
Spring Boot 3+ TLDR
#Configuration
public class WebConfiguration implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseTrailingSlashMatch(true);
}
}
}
Be sure to use as above for Spring Boot 3+ (that uses Spring 6)
to have URL ending with '/' still being processed by mapping in Controllers without ending '/'.
Longer answer:
Note for the snippet the value is exactly true
and #EnableWebMvc is not used with Spring Boot
(as it would in fact disable autoconfiguration for web MVC)
That was exactly recommended in
https://github.com/spring-projects-experimental/spring-boot-migrator/issues/206
"Spring Boot 3.0.0 M4 Release Notes"
And because there are reasons why Spring became less forgiving,
( see "Deprecate trailing slash match and change default value from true to false"
https://github.com/spring-projects/spring-framework/issues/28552 )
I think we should better to think as bad habit to use URLs ending with '/', that now require extra attention.

How to override a built-in exception mapper in Jersey 2.23?

In one of my projects I've already upgraded Jersey from version 2.14 to 2.23. But I'm struggling many hours with one problem. My project defines its own ExceptionMapper for a ValidationException, but unfortunately Jersey already has a built-in exception mapper for this exception and I cannot override it.
I have registered correctly (I checked it) my own mapper which is presented below:
#Provider
public class ValidationExceptionMapper implements
ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
but it is never being called. Jersey always pick up the org.glassfish.jersey.server.validation.internal.ValidationExceptionMapper.
I've also tried to use #Priority annotation for my custom mapper, but unfortunately Jersey doesn't take it into account.
So what is going on? It worked perfectly fine in the previous Jersey version, so it seems to be a regression bug.
I give up. Any clues?
It really turned out to be a regression bug in Jersey, introduced in January 2015.
Bug is related with two Jersey's extensions: for Weld and bean validation.
Because without Weld container started, my custom ValidationExceptionMapper mapper takes precedence over the built-in one provided by the jersey-bean-validation module, so my goal is achieved.
I've filled a bug report under JERSEY-3153, later moved as the issue #3425.
To be honest, I'm never ever going to use Weld + Jersey again... I'm so tired with this combination. Through the last two years I've encountered around 10 bugs already. I'm really tired.
Anyway, I hope it will help somebody.
UPDATE:
As #Justin Jose noticed in the comments below, there is also another workaround for the mentioned bug. We can use HK2 bindings, to override the problematic built-in mapper:
register(new AbstractBinder() {
#Override
protected void configure() {
bind(my.custom.ValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class);
}
});
Jersey's built-in ValidationExceptionMapper is registered via ValidationFeature. Probably, replacing Jersey's ValidationFeature with your own version can do the trick. It can be done as follows.
Firstly, disable auto-discoverable ValidationFeature
property(ServerProperties.BV_FEATURE_DISABLE, true);
Next step is to register a clone of Jersey's validation feature
public static class ValidationFeatureClone implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new ValidationBinder());
context.register(NewValidationExceptionMapper.class);
context.register(ValidationErrorMessageBodyWriter.class);
return true;
}
}
In the clone, you should specify your new ExceptionMapper.
Finally, register your new Feature
register(ValidationFeatureClone.class)
UPDATE:
From Jersey 2.20 onwards, default ValidationExceptionMapper can be overwritten using HK2 binding as shown below.
register(new AbstractBinder() {
#Override
protected void configure() {
bind(NewValidationExceptionMapper.class).to(ExceptionMapper.class)
.in(Singleton.class).ranked(10‌​);
}
});
I found a way to get it to work with newer Jersey releases again, which I also posted under your bug report.
One needs to build Jersey locally with the changed code, specifically the jersey-bean-validation artifact.
Locate org.glassfish.jersey.server.validation.internal.ValidationBinder and comment out the following two lines in configure():
bind(ValidationExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
bind(ValidationErrorMessageBodyWriter.class).to(MessageBodyWriter.class).in(Singleton.class);
It's kind of ironic that the source code comment above those lines says that they're supposed to allow users to register their own providers.
Unfortunately the bug still exists and cause headaches...
In my case easiest solution was providing custom ExceptionMapper specifically for ConstraintViolationException.
public class CVExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(final Throwable t) {
...
}
}
and then registering it as always with:
context.register(CVExceptionMapper.class);

Is it possible to set interceptor with annotation in Spring?

I am trying to stay declarative, convention-base and XML-less.
So, I have no web.xml, and I have no context configuration XMLs. Unfortunately, Google is spammed with old-fashion Spring examples and it is impossible to find modern answer.
My question is: is it possible to declare interceptor with annotations in Spring? Apparently it would be possible to do the same way as it done with controllers (controller class is annotated with #Controller and it's methods -- with #RequestMapping).
The best way I found is here https://stackoverflow.com/a/16706896/258483
Unfortunately, it is not declarative.
Using Java configuration and the #Configuration annotation it looks like you create an interceptor and register it as described here. It's not as simple as annotating a class as an interceptor but may still adhere to your stipulations.
EDIT:
In java configuration class, we need to extend
WebMvcConfigurerAdapter. To add our interceptor, we override
WebMvcConfigurerAdapter. addInterceptors() method. Find the code
snippet.
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
registry.addInterceptor(new TransactionInterceptor()).addPathPatterns("/person/save/*");
}

What does configureDefaultServletHandling means?

I am trying to understand how does Spring MVC works, and I don't understand this part of code in my Spring configurations:
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
When this is in my WebContextApplication class, everything works fine and when it's not present everything works fine too. So what is the purpose of this method? Should my WebContextApplication class have this method? and why?
As JB Nizet already tried to explain both are used to serve static resources.
So your question is that your Java based Spring configuration has
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("/resources/bootstrap/");
}
then why do you need
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
or why
<mvc:default-servlet-handler/> if you have
<mvc:resources mapping="/assets/**" location="/resources/bootstrap/" />
in terms of xml configuration.
To answer your question based on the requirements you have put you don't need to override configureDefaultServletHandling() as you have already overridden and provided your static resource mappings.
By overriding addResourceHandlers() method you are essentially asking ResourceHttpRequestHandler to serve the resources mentioned resource location.
However if you override configureDefaultServletHandling() and enabling it you are essentially asking default servlet (mapped to "/") to serve the resources. There are couple of things you need to take care here if you are using this. Quoting from docs -
This allows for mapping the DispatcherServlet to "/" (thus overriding the mapping of the container’s default Servlet), while still allowing static resource requests to be handled by the container’s default Servlet. It configures a DefaultServletHttpRequestHandler with a URL mapping of "/**" and the lowest priority relative to other URL mappings.
This handler will forward all requests to the default Servlet. Therefore it is important that it remains last in the order of all other URL HandlerMappings. That will be the case if you use or alternatively if you are setting up your own customized HandlerMapping instance be sure to set its order property to a value lower than that of the DefaultServletHttpRequestHandler, which is Integer.MAX_VALUE.

Categories