如何为所有控制器配置默认的@RestController URI前缀?

我知道您可以在application.properties设置server.contextPath来更改根上下文。

另外,我可以在Spring Boot的应用程序配置中添加一个额外的上下文,如下例(在Groovy中),为根上下文的URL映射添加“/ api”:

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

我正在尝试为Web服务调用提供一个单独的基本URI“/ api”,我可以利用它来保证安全性等。但是使用上述方法将意味着我可以通过以下方式访问我的任何URI,无论是否使用Web服务“/”或“/ api”,并没有提供具体的隔离。

是否有人知道使用配置为所有@RestController设置基本路径的更好方法,而不必使用/ api /正式为每个控制器添加前缀? 如果我被迫手动为每个控制器的URI添加前缀,则可能会错误地省略它并绕过我特定于Web服务的安全措施。

这是Stack Overflow中对同一类型问题的引用,但从未完全回答过:

Spring Boot:为RestControllers配置url前缀

有人在Spring MVC Jira中提出了一个问题,并提出了一个很好的解决方案,我现在正在使用它。 我们的想法是在每个RestController文件中使用前缀中的Spring Expression Language,并引用Spring Boot application.properties文件中的单个属性。

以下是该问题的链接: https : //jira.spring.io/browse/SPR-13882

自Spring Boot 1.4.0.RC1以来,有一种解决此类问题的新解决方案(详见https://github.com/spring-projects/spring-boot/issues/5004 )

Shahin ASkari的解决方案禁用部分Auto配置,因此可能会导致其他问题。

以下解决方案采用了他的想法并将其正确地集成到弹簧启动中。 对于我的情况,我希望所有RestControllers都具有基本路径api,但仍然使用根路径提供静态内容(fe angular webapp)

编辑 :我在博客文章中对其进行了总结,其中略有改进版本,请参阅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); } }; } }; } } 

我有同样的担忧,并且由于记录的问题而不是Spring EL选项的粉丝,我希望在控制器中严格控制前缀,但我不想依赖开发人员做正确的事情。

这些天可能有更好的方法,但这就是我所做的。 你们有没有看到任何缺点,我仍然在测试任何副作用。

  1. 定义自定义注释。
    这允许开发人员显式提供类型化的属性,例如int apiVersion(),String resourceName()。 这些值将在以后作为前缀的基础。
  2. 带有此新注释的带注释的休止控制器
  3. 实现了自定义RequestMappingHandlerMapping

在RequestMappingHandlerMapping中,我可以读取自定义注释的属性并根据需要修改最终的RequestMappingInfo。 以下是一些代码段:

 @Configuration public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport { @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { return new MyCustomRequestMappingHandlerMapping(); } } 

在MyCustomRequestMappingHandlerMapping中,覆盖registerHandlerMethod:

 private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping { private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class); public MyCustomRequestMappingHandlerMapping() { super(); } @Override protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { // find the class declaring this method Class beanType = method.getDeclaringClass(); // check for the My rest controller annotation MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class); if (myRestAnnotation != null) { // this is a My annotated rest service, lets modify the URL mapping PatternsRequestCondition oldPattern = mapping.getPatternsCondition(); // create a pattern such as /api/v${apiVersion}/${resourceName} String urlPattern = String.format("/api/v%d/%s", myRestAnnotation.apiVersion(), myRestAnnotation.resourceName()); // create a new condition PatternsRequestCondition apiPattern = new PatternsRequestCondition(urlPattern); // ask our condition to be the core, but import all settinsg from the old // pattern PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern); myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}", beanType, myRestAnnotation, oldPattern, updatedFinalPattern); mapping = new RequestMappingInfo( mapping.getName(), updatedFinalPattern, mapping.getMethodsCondition(), mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(), mapping.getProducesCondition(), mapping.getCustomCondition() ); } super.registerHandlerMethod(handler, method, mapping); } } 

略微不那么详细的解决方案,它不会复制检查注释的逻辑,只会改变映射路径:

 private static final String API_PREFIX = "api"; @Bean WebMvcRegistrationsAdapter restPrefixAppender() { return new WebMvcRegistrationsAdapter() { @Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return new RequestMappingHandlerMapping() { @Override protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType); if (mappingForMethod != null) { return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod); } else { return null; } } }; } }; } 

副作用

您的错误控制器也将映射到/ api / error,这会中断error handling(DispatcherServlet仍然会将错误重定向到/ error而没有前缀!)。

可能的解决方案是在上面的代码中添加/ api前缀时跳过/错误路径(再多一个“if”)。