Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make @Controller mapping path configurable?

I'm building an internal library that should automatically add a few controllers to Spring MVC application. These controllers are all @RestController with a few handler methods annotated with @RequestMapping. Since it's a library, I want users to be able to specify the root path where the library should expose all these controllers. Illustration:

// given that I have a controller like this:
@RestController
@RequestMapping("/admin") 
class AdminController {
  @RequestMapping("/users")
  UsersDto allUsers() { ... }
}

// it will be exposed at `http://localhost/admin/users`

What I want to achieve is to make /admin part configurable. For example, if user says in application.properties:

super.admin.path=/top/secret/location/here

I want AdminController's handlers to be available at http://localhost/top/secret/location/here, and so the allUsers() handler should have a full path of:

http://localhost/top/secret/location/here/users

How do I achieve this? Feels like a pretty common task, but I didn't manage to find a straightforward solution that works.

My finding #1

There's a SimpleUrlHandlerMapping mechanism that seems to be exactly what I want:

@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
    SimpleUrlHandlerMapping simpleUrlHandlerMapping = new SimpleUrlHandlerMapping();
    simpleUrlHandlerMapping.setOrder(Integer.MAX_VALUE - 2);
    simpleUrlHandlerMapping.setUrlMap(Collections.singletonMap("/ANY_CUSTOM_VALUE_HERE/*", "adminController"));
    return simpleUrlHandlerMapping;
}

But it keeps saying

The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler

My finding #2

There's Spring Boot Admin project that has this exact feature - you may configure where they should expose all their endpoints. They seem to have this functionality implemented from scratch in PrefixHandlerMapping. How they use it:

...
@Bean
public PrefixHandlerMapping prefixHandlerMappingNotificationFilterController() {
    PrefixHandlerMapping prefixHandlerMapping = new PrefixHandlerMapping(notificationFilterController());
    prefixHandlerMapping.setPrefix(adminServerProperties.getContextPath());
    return prefixHandlerMapping;
}
...
like image 854
Andrey Agibalov Avatar asked May 29 '16 17:05

Andrey Agibalov


1 Answers

In addition to @M. Deinum solution, you can use Path Patterns with Placeholders. As Spring documentation states:

Patterns in @RequestMapping annotations support ${…​} placeholders against local properties and/or system properties and environment variables. This may be useful in cases where the path a controller is mapped to may need to be customized through configuration.

So in your case, your controller would be like:

@RestController
@RequestMapping("/${super.admin.path:admin}") 
class AdminController {
  // Same as before
}

The preceding controller would use super.admin.path local/system property or environment variable value as its prefix or admin if those aren't provided. If you're using Spring Boot, by adding the following to your application.properties:

super.admin.path=whatever

You can customize that prefix.

like image 140
Ali Dehghani Avatar answered Oct 09 '22 04:10

Ali Dehghani