Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

In Spring Boot applications, every controller can have its own URL mapping. This makes it easy for a single application to provide web endpoints at multiple locations. For example, we can group our API endpoints into logical groupings such as internal and external.

However, there may be times when we want all of our endpoints under a common prefix. In this tutorial, we'll look at different ways to use a common prefix for all Spring Boot controllers.

2. Servlet Context

The main component responsible for handling web requests in Spring applications is the DispatcherServlet. By customizing this component, we have a fair amount of control over how requests are routed.

Let's take a look at two different ways to customize the DispatcherServlet that will make all of our application endpoints available at a common URL prefix.

2.1. Spring Bean

The first way is by introducing a new Spring bean:

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

Here, we're creating a ServletRegistrationBean that wraps the DispatcherServlet bean. Notice that we provide an explicit base URL of /api/. This means all of our endpoints must be accessed at that base URL prefix.

2.2. Application Properties

We can also achieve the same result just by using application properties. In versions of Spring Boot after 2.0.0, we would add the following to our application.properties file:

server.servlet.contextPath=/api

Prior to that version, the property name is slightly different:

server.contextPath=/api

One benefit of this approach is that it only uses normal Spring properties. This means we can easily change or override our common prefix using standard mechanisms like profiles or external property bindings.

2.3. Pros and Cons

The main benefit of these two approaches is also the main downside: They affect every endpoint in the application.

For some applications, this may be perfectly fine. However, some applications may need to use standard endpoint mappings to interact with third-party services – for example, OAuth exchanges. In these cases, a global solution like this may not be a good fit.

3. Annotations

Another way we can add a prefix to all of the controllers in a Spring application is using annotations. Below, we'll look at two different approaches.

3.1. SpEL

The first way involves using Spring Expression Language (SpEL) with the standard @RequestMapping annotation. With this approach, we simply add a property to each controller that we want to prefix:

@Controller
@RequestMapping(path = "${apiPrefix}/users")
public class UserController {

} 

Then, we simply specify the property value in our application.properties:

apiPrefix=/api

3.2. Custom Annotation

Another way to achieve this is by creating our own annotation:

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

Then, we only need to apply the annotation to each controller we want to prefix:

@Controller
@ApiPrefixController
public class SomeController {
    @RequestMapping("/users")
    @ReponseBody
    public String getAll(){
        // ...
    }
}

3.3. Pros and Cons

These two approaches address the main concern of the previous method: They both offer fine-grained control over which controllers get the prefix. We can apply the annotations to specific controllers only, rather than impacting all endpoints in the application.

4. Server-Side Forward

One final way we will look at is using a server-side forward. Unlike a redirect, a forward does not involve a response back to the client. This means our application can pass requests between endpoints without affecting the client.

To get started, let's write a simple controller with two endpoints:

@Controller
class EndpointController {
    @GetMapping("/endpoint1")
    @ResponseBody
    public String endpoint1() {
        return "Hello from endpoint 1";
    }

    @GetMapping("/endpoint2")
    @ResponseBody
    public String endpoint2() {
        return "Hello from endpoint 2";
    }
}

Next, we create a new controller that is based on the prefix we want:

@Controller
@RequestMapping("/api/endpoint")
public class ApiPrefixController {

    @GetMapping
    public ModelAndView route(ModelMap model) {
        if(new Random().nextBoolean()) {
            return new ModelAndView("forward:/endpoint1", model);
        } 
        else {
            return new ModelAndView("forward:/endpoint2", model);
        }
    }
}

This controller has a single endpoint that acts as a router. In this case, it essentially flips a coin to forward the original request to one of our other two endpoints.

We can verify it's working by sending a few consecutive requests:

> curl http://localhost:8080/api/endpoint
Hello from endpoint 2
> curl http://localhost:8080/api/endpoint
Hello from endpoint 1
> curl http://localhost:8080/api/endpoint
Hello from endpoint 1
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2

The main benefit of this approach is that it is very powerful. We can apply any logic we want to determine how to forward a request: URL path, HTTP method, HTTP headers, and so on.

5. Conclusion

In this article, we've learned several ways to apply a common prefix to every controller in a Spring application. As with most decisions, each approach comes with pros and cons that should be carefully considered before implementation.

As always, the code examples in this tutorial can be found over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
1 Comment
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!