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.
I have a spring boot app serving angular resources in my static [resources/static] folder. I also am using the same project to serve my JSON-REST-API endpoints.
Hence I have defined my REST api under localhost:9090/api/...
My Angular2 app-build is served under localhost:9090/ui/... via static resources
Now I want to forward all my ui/** urls to ui/index.html/**
How do I do this?
P.S. I have introduced a custom static resource url pattern
spring.mvc.static-path-pattern=/ui/**
Then all my ui/** request will look to the static/**
This way I was able to secure my /api/** and "permitAll" ui/** requests
This simple configuration will do the trick and will even refresh your # routes in angular 2 if enabled.
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ui").setViewName("forward:/ui/index.html");
registry.addViewController("/ui/").setViewName("forward:/ui/index.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
Pointers:
- #SpringBootApplication should be annotated where the spring boot app is run
- Do not have #EnableWebMvc as this breaks all auto configuration done by spring boot
- See whether this configuration is under a directory you have marked for
#ComponentScan({"com.foo.config"})
- This implementation is specifically tailored for situations where you have a custom spring.mvc.static-path-pattern defined [ui/]
I'm trying to to mix mvc and rest in a single spring boot project.
I want to set base path for all rest controllers (eg. example.com/api)
in a single place (I don't want annotate each controller with #RequestMapping('api/products'), instead, just #RequestMapping('/products').
Mvc controllers should be accessible by example.com/whatever
Is it possible?
(I don't use spring data rest, just spring mvc)
With Spring Boot 1.2+ (<2.0) all it takes is a single property in application.properties:
spring.data.rest.basePath=/api
ref link : https://docs.spring.io/spring-data/rest/docs/current/reference/html/#getting-started.changing-base-uri
For 2.x, use
server.servlet.context-path=/api
A bit late but the same question brought me here before reaching the answer so I post it here.
Create (if you still don't have it) an application.properties and add
server.contextPath=/api
So in the previous example if you have a RestController with #RequestMapping("/test") you will access it like localhost:8080/api/test/{your_rest_method}
question source: how do i choose the url for my spring boot webapp
For spring boot framework version 2.0.4.RELEASE+. Add this line to application.properties
server.servlet.context-path=/api
Try using a PathMatchConfigurer (Spring Boot 2.x):
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("api", HandlerTypePredicate.forAnnotation(RestController.class));
}
}
I couldn't believe how complicate the answer to this seemingly simple question is. Here are some references:
Spring JIRA Ticket
Another SO question
Yet another SO question
Very nice GitRepository that showcases the problem
There are many differnt things to consider:
By settingserver.context-path=/api in application.properties you can configure a prefix for everything.(Its server.context-path not server.contextPath !)
Spring Data controllers annotated with #RepositoryRestController that expose a repository as rest endpoint will use the environment variable spring.data.rest.base-path in application.properties. But plain #RestController won't take this into account. According to the spring data rest documentation there is an annotation #BasePathAwareController that you can use for that. But I do have problems in connection with Spring-security when I try to secure such a controller. It is not found anymore.
Another workaround is a simple trick. You cannot prefix a static String in an annotation, but you can use expressions like this:
#RestController
public class PingController {
/**
* Simple is alive test
* #return <pre>{"Hello":"World"}</pre>
*/
#RequestMapping("${spring.data.rest.base-path}/_ping")
public String isAlive() {
return "{\"Hello\":\"World\"}";
}
}
Since this is the first google hit for the problem and I assume more people will search for this. There is a new option since Spring Boot '1.4.0'.
It is now possible to define a custom RequestMappingHandlerMapping that allows to define a different path for classes annotated with #RestController
A different version with custom annotations that combines #RestController with #RequestMapping can be found at this blog post
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
if (AnnotationUtils.findAnnotation(beanType, RestController.class) != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
You can create a custom annotation for your controllers:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#RestController
#RequestMapping("/test")
public #interface MyRestController {
}
Use it instead of the usual #RestController on your controller classes and annotate methods with #RequestMapping.
Just tested - works in Spring 4.2!
For Boot 2.0.0+ this works for me: server.servlet.context-path = /api
I found a clean solution, which affects only rest controllers.
#SpringBootApplication
public class WebApp extends SpringBootServletInitializer {
#Autowired
private ApplicationContext context;
#Bean
public ServletRegistrationBean restApi() {
XmlWebApplicationContext applicationContext = new XmlWebApplicationContext();
applicationContext.setParent(context);
applicationContext.setConfigLocation("classpath:/META-INF/rest.xml");
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/rest/*");
servletRegistrationBean.setName("restApi");
return servletRegistrationBean;
}
static public void main(String[] args) throws Exception {
SpringApplication.run(WebApp.class,args);
}
}
Spring boot will register two dispatcher servlets - default dispatcherServlet for controllers, and restApi dispatcher for #RestControllers defined in rest.xml:
2016-06-07 09:06:16.205 INFO 17270 --- [ main] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'restApi' to [/rest/*]
2016-06-07 09:06:16.206 INFO 17270 --- [ main] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
The example rest.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="org.example.web.rest"/>
<mvc:annotation-driven/>
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</beans>
But, you're not limited to:
use XmlWebApplicationContext, you may use any else context type available, ie. AnnotationConfigWebApplicationContext, GenericWebApplicationContext, GroovyWebApplicationContext, ...
define jsonMessageConverter, messageConverters beans in rest context, they may be defined in parent context
I did some research on the differences of spring properties mentioned in this thread. Here are my findings if anybody is wondering.
spring.data.rest.basePath Property
spring.data.rest.basePath=/api
This property is specifically for Spring Data Rest projects. It won't work in a usual Spring MVC projects.
To change the context path in MVC projects, you can use those two properties mentioned below. Let me mention the differences too.
server.servlet.context-path Property
server.servlet.context-path=/api
This one sets the context path on your web servelet. This property perfectly works fine in both spring mvc and spring data rest projects. But, the differnce is the request url will be filter out before reaching spring interceptors. So it will respond with HTML on bad request. Not Spring's or your own custom JSON response (in #ResponseBodyAdvice annotated class) defined. To overcome that, you should use this property below.
spring.mvc.servlet.path Property
spring.mvc.servlet.path=/api
This will filter the request URL in spring mvc interceptors and will respond default/your custom JSON response if you invoke a bad request.
Conclusion:
So as the OP's question, I would suggest that he should use spring.mvc.servlet.path to change the context path.
I might be a bit late, BUT... I believe it is the best solution. Set it up in your application.yml (or similar config file):
spring:
data:
rest:
basePath: /api
As I can remember that's it - all of your repositories will be exposed beneath this URI.
You can create a base class with #RequestMapping("rest") annotations and extend all you other classes with this base class.
#RequestMapping("rest")
public abstract class BaseController {}
Now all classes that extend this base class will be accessible at rest/**.
With spring-boot 2.x you can configure in application.properties:
spring.mvc.servlet.path=/api
For those who use YAML configuration(application.yaml).
Note: this works only for Spring Boot 2.x.x
server:
servlet:
contextPath: /api
If you are still using Spring Boot 1.x
server:
contextPath: /api
server.servlet.context-path=/api would be the solution I guess. I had the same issue and this got me solved. I used server.context-path. However, that seemed to be deprecated and I found that server.servlet.context-path solves the issue now. Another workaround I found was adding a base tag to my front end (H5) pages. I hope this helps someone out there.
Cheers
You can create a custom annotation for your controllers:
Use it instead of the usual #RestController on your controller classes and annotate methods with #RequestMapping.
Works fine in Spring 4.2!
For Spring WebFlux the approach is similar to Harald's, but with the obvious WebFlux configuration set up:
#Configuration
public class WebFluxConfig implements WebFluxConfigurer {
#Override
public void configurePathMatching(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
}
}
And for Kotlin it's:
#Configuration
class WebFluxConfig : WebFluxConfigurer {
override fun configurePathMatching(configurer: PathMatchConfigurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
}
This solution applies if:
You want to prefix RestController but not Controller.
You are not using Spring Data Rest.
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new ApiAwareRequestMappingHandlerMapping();
}
private static class ApiAwareRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private static final String API_PATH_PREFIX = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
if (AnnotationUtils.findAnnotation(beanType, RestController.class) != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_PATH_PREFIX)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern, mapping.getMethodsCondition(),
mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
}
}
This is similar to the solution posted by mh-dev, but I think this is a little cleaner and this should be supported on any version of Spring Boot 1.4.0+, including 2.0.0+.
Per Spring Data REST docs, if using application.properties, use this property to set your base path:
spring.data.rest.basePath=/api
But note that Spring uses relaxed binding, so this variation can be used:
spring.data.rest.base-path=/api
... or this one if you prefer:
spring.data.rest.base_path=/api
If using application.yml, you would use colons for key separators:
spring:
data:
rest:
basePath: /api
(For reference, a related ticket was created in March 2018 to clarify the docs.)
worked server.contextPath=/path
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.
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.