103

I have controller mappings to /user and /order:

@RestController
@RequestMapping("/users")
public class UserController {
    ...
}

@RestController
@RequestMapping("/orders")
public class OrderController {
    ...
}

I want to access these by URL at http://localhost:8080/api/users and http://localhost:8080/api/orders, respectively.

How do I achieve this in Spring Boot?

tkruse
  • 10,222
  • 7
  • 53
  • 80
Murali
  • 3,412
  • 5
  • 36
  • 34
  • 1
    possible duplicate of [Spring Boot Context Root](http://stackoverflow.com/questions/20405474/spring-boot-context-root) – M. Deinum Jan 18 '15 at 11:05

11 Answers11

82

You can provide a mapping to root context path of your spring boot application to /api/* in your custom configuration.

import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;

@Configuration
public class DispatcherServletCustomConfiguration {

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public ServletRegistrationBean dispatcherServletRegistration() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
                dispatcherServlet(), "/api/");
        registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
        return registration;
    }
}

or add this to your application.properties in src\main\resources folder

server.contextPath=/api

EDIT

As of Spring Boot 2.x the property has been deprecated and should be replaced with

server.servlet.contextPath=/api

More you find here Spring Boot Context Root and here Add servlet mapping to DispatcherServlet

Hannes Schneidermayer
  • 4,729
  • 2
  • 28
  • 32
Tomasz Janek
  • 1,135
  • 9
  • 5
  • 87
    this adds the prefix to everything, not just the controllers – CESCO Nov 04 '15 at 23:28
  • Both work great when wanting to add a context path. The two issues that I faced was that creating a DispatcherServelt bean also meant that the /oauth routes were under the context path which falls outside of RESTful standards; and using contextPath in the configuration introduced the same issue as well as CORS and tests not being aware of the context path at all. I ended up adding the @RequestMapping annotation to each controller as necessary. – jaseeey Mar 09 '16 at 22:10
  • 18
    It should just be "server.contextPath=/api/" . Having a trailing * is causing error in Spring Boot version 1.3.5 – Anand Jun 28 '16 at 12:46
  • "server.context-path=/api/" works for me. Many Thanks! – Clement.Xu Oct 14 '16 at 03:59
  • 1
    Please note, org.springframework.boot.context.embedded.ServletRegistrationBean was deprecated in 1.4 and its replacement, org.springframework.boot.web.servlet.ServletRegistrationBean, was introduced. The deprecated class was removed in 1.5 – binary Nov 07 '17 at 14:14
  • Note that for spring 2.0 it is server.servlet.contextPath: /xxx – kboom Apr 12 '18 at 07:11
  • 1
    Is this working for Spring Boot 2.0? It doesn't seem to. – GarouDan Jun 02 '18 at 16:25
  • 6
    The `server.servlet.context-path = /api/*` sitting in the `application.properties` file was giving me the `Error registering Tomcat:type=TomcatEmbeddedWebappClassLoader,host=localhost,context=/api/* - ContextPath must start with '/' and not end with '/'` error. The `server.servlet.context-path = /api` property without quotes was the one required for the application to start. – Stephane Jul 13 '18 at 11:57
  • 8
    For Spring Boot 2.0 it must be like `server.servlet.context-path=/api`. – Halil İbrahim Oymacı Feb 04 '19 at 15:30
62

If you want to add prefix just for some controllers I found two others solutions

Option 1 - Use spring SpEL to add a prefix variable for your controllers

@RestController
@RequestMapping(path = "${v1API}/users")
public class V1FruitsController {

    @GetMapping(path = "")
    @ResponseBody
    public String list(){
        return "[\"Joe\", \"Peter\"]";
    }
}

application.properties

v1API=/api/v1

Option 2 - Create a custom controller annotation

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@RequestMapping("/api/v1")
public @interface V1APIController {
    @AliasFor(annotation = Component.class)
    String value() default "";
}


@V1APIController
public class UserController {

    @RequestMapping("/users")
    @ReponseBody
    public String index(){
        return "[\"Joe\", \"Peter\"]";
    }
}

then test it

curl -X GET localhost:8080/api/v1/users
deFreitas
  • 4,196
  • 2
  • 33
  • 43
20

If you are using spring boot 2 (spring framework 5), there is a replacement of the property in your application.properties:

server.contextPath

for:

server.servlet.context-path=
Eduardo
  • 2,070
  • 21
  • 26
13

Add your default path in the application.properties as:

server.servlet.contextPath=/mainPath

Here /mainPath will be the prefix for all the controller

Bishal Jaiswal
  • 1,684
  • 13
  • 15
12

In addition to the other comments about changing the application property for the context path, you can also use an application property to set the prefix for the dispatcher servlet alone, in Spring Boot 2.3.1.

spring.mvc.servlet.path=/api

The request mappings would not change in your controllers. While context path moves the entire application to a different path, servlet path only limits the URLs that are handled by the dispatcher servlet. The servlet path is the equivalent of the servlet mapping in web.xml. Other resources that do not use the dispatcher servlet can be accessed from any other URL.

If you have other controllers that are not mapped to the /api prefix, then this will not work, unless you declare a second dispatcher servlet with a different prefix for those controllers.

downeyt
  • 1,206
  • 2
  • 12
  • 23
3

For those interested, here is a Kotlin take on deFreitas' Option 2 Component as I was unable to use spring.data.rest.basePath or server.servlet.contextPath in application.yaml. (This is with Spring Boot 2.1.2 and Kotlin 1.13.11)

package com.myproject.controller

import org.springframework.core.annotation.AliasFor
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestMapping

import kotlin.annotation.MustBeDocumented
import kotlin.annotation.Retention
import kotlin.annotation.Target
import kotlin.annotation.AnnotationRetention

@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component
@RequestMapping("/api/v1")
annotation class V1ApiController(
    @get:AliasFor(annotation = Component::class)
    val value: String = ""
)

If you're using IntelliJ, optimizing imports will probably remove the Kotlin annotation imports for brevity.

geeves
  • 99
  • 2
  • 4
3

In application.yml add this:

server:
  servlet:
    context-path: "/contextPath"
Alex
  • 454
  • 5
  • 5
3

Since spring-webmvc-5.1.x

import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerTypePredicate;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("/api",
                HandlerTypePredicate.forAnnotation(RestController.class));
    }
}
wk.
  • 71
  • 3
  • 1
    Thanks, this is exactly what I needed! Only for classes which are annotated with @RestController. This allows me to separate between a webjar frontend and the api. – NSV Apr 23 '23 at 09:54
1

Add this at application.properties

server.servlet.context-path=/api/v1/
S3D
  • 109
  • 1
  • 9
0

server.servlet.context-path is the correct path. Not server.servlet.contextPath, and unfortunately it doesn't seem to support lists which you could do in web.xml like this:

    <servlet>
        <description>Servlet used by Spring MVC to handle all requests into the application</description>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/app1/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/app2/*</url-pattern>
    </servlet-mapping>
Gary Tessman
  • 433
  • 4
  • 3
0

Additional. If you use .yaml, you could write it as:

server:
  servlet:
    context-path: /api
troy
  • 2,145
  • 2
  • 23
  • 31