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;
}
...
In addition to @M. Deinum solution, you can use Path Patterns with Placeholders. As Spring documentation states:
So in your case, your controller would be like:
The preceding controller would use
super.admin.path
local/system property or environment variable value as its prefix oradmin
if those aren't provided. If you're using Spring Boot, by adding the following to yourapplication.properties
:You can customize that prefix.