Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure a default @RestController URI prefix for all controllers?

I know you can set the server.contextPath in application.properties to change the root context.

Also, I can add an additional context in the application config for Spring Boot like the following example (in Groovy) to add an "/api" to the URL mappings of the root context:

@Bean
ServletRegistrationBean dispatcherServlet() {
     ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
        reg.name = "dispatcherServlet"
        reg.addInitParameter("contextConfigLocation", "")
        reg.addUrlMappings("/api/*")
        reg.loadOnStartup = 2
        reg
    }
}

I am trying to have a separate base URI "/api" specifically for web service calls, that I can leverage for security, etc. However using the above approach will mean that any of my URIs, web service or not, can be reached with "/" or "/api", and provides no concrete segregation.

Is anyone aware of a better approach to set a base path for all @RestController(s) using configuration, without having to formally prefix every controller with /api/? If I am forced to manually prefix the URI for each controller, it would be possible to mistakenly omit that and bypass my security measures specific to web services.

Here is a reference in Stack Overflow to the same type of question, which was never completely answered:

Spring Boot: Configure a url prefix for RestControllers

like image 746
pczeus Avatar asked Jan 14 '16 22:01

pczeus


People also ask

How do you specify prefix for all controllers in spring boot?

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(){ // ... } }

Can we replace @RestController with @controller?

In simple words, @RestController is @Controller + @ResponseBody. So if you are using latest spring version you can directly replace @Controller with @RestController without any issue.

What is @RestController and @controller?

@Controller is used to mark classes as Spring MVC Controller. @RestController annotation is a special controller used in RESTful Web services, and it's the combination of @Controller and @ResponseBody annotation. It is a specialized version of @Component annotation.

Can we have multiple RestController?

So what happens when you have two rest controller defined onto the same path? If you don't have any overlapping request mappings other than the code being slightly confusing, nothing will actually go wrong and you can successfully send requests to the methods inside each controller.


2 Answers

In continuation to the currently accepted solution the github issue addresses the same.

Spring 5.1 and above you can implement WebMvcConfigurer and override configurePathMatch method like below

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("/api",
                    HandlerTypePredicate.forAnnotation(RestController.class));
    }

}

Now all the @RestControllers will have /api as the prefix path alongside the path configured.

Official Documentation

like image 53
Hegdekar Avatar answered Sep 30 '22 07:09

Hegdekar


There's a new solution to solve this kind of problem available since Spring Boot 1.4.0.RC1 (Details see https://github.com/spring-projects/spring-boot/issues/5004)

The solution of Shahin ASkari disables parts of the Auto configuration, so might cause other problems.

The following solution takes his idea and integrates it properly into spring boot. For my case I wanted all RestControllers with the base path api, but still serve static content with the root path (f.e. angular webapp)

Edit: I summed it up in a blog post with a slightly improved version see https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/

@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();
                        RestController restApiController = beanType.getAnnotation(RestController.class);
                        if (restApiController != 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);
                    }
                };
            }
        };
    }

}
like image 25
mh-dev Avatar answered Sep 30 '22 08:09

mh-dev