How to set base url for rest in spring boot?

2020-01-25 05:00发布

问题:

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 accessable by example.com/whatever

Is it possible?

(I don't use spring data rest, just spring mvc)

回答1:

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


回答2:

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



回答3:

For spring boot framework version 2.0.4.RELEASE+. Add this line to application.properties

server.servlet.context-path=/api


回答4:

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);
                    }
                };
            }
        };
    }
}


回答5:

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:

  1. By settingserver.context-path=/api in application.properties you can configure a prefix for everything.(Its server.context-path not server.contextPath !)
  2. 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\"}";
  }
}


回答6:

For Boot 2.0.0+ this works for me: server.servlet.context-path = /api



回答7:

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


回答8:

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.



回答9:

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!



回答10:

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/**.



回答11:

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));
    }
}


回答12:

With spring-boot 2.x you can configure in application.properties:

spring.mvc.servlet.path=/api


回答13:

This solution applies if:

  1. You want to prefix RestController but not Controller.
  2. 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+.



回答14:

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.)



回答15:

worked server.contextPath=/path