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)
This question is related to
java
spring
rest
spring-boot
spring-mvc
With Spring Boot 1.2+ (<2.0) all it takes is a single property in application.properties:
spring.data.rest.basePath=/api
For 2.x, use
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:
XmlWebApplicationContext
, you may use any else context type available, ie. AnnotationConfigWebApplicationContext
, GenericWebApplicationContext
, GroovyWebApplicationContext
, ...jsonMessageConverter
, messageConverters
beans in rest context, they may be defined in parent contextFor Boot 2.0.0+ this works for me: server.servlet.context-path = /api
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!
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);
}
};
}
};
}
}
This solution applies if:
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+.
With spring-boot 2.x you can configure in application.properties:
spring.mvc.servlet.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
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/**
.
For spring boot framework version 2.0.4.RELEASE+
. Add this line to application.properties
server.servlet.context-path=/api
worked server.contextPath=/path
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.)
I couldn't believe how complicate the answer to this seemingly simple question is. Here are some references:
There are many differnt things to consider:
server.context-path=/api
in application.properties
you can configure a prefix for everything.(Its server.context-path not server.contextPath !)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\"}";
}
}
I might be a bit late, BUT... I believe it is the best solution. Set it up in your application.yml (or analogical config file):
spring:
data:
rest:
basePath: /api
As I can remember that's it - all of your repositories will be exposed beneath this URI.
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
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
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!
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));
}
}
Source: Stackoverflow.com