2016-01-14 21 views
11

我知道你可以在application.properties中設置server.contextPath來更改根上下文。如何爲所有控制器配置默認的@RestController URI前綴?

而且,我還可以添加在應用程序配置爲Spring引導像下面的例子(在Groovy)的附加上下文到「/ API」添加到根上下文的URL映射:

@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(s)設置基路徑的更好方法,而無需用/ api /?正式地爲每個控制器加前綴。如果我被迫爲每個控制器手動添加URI,那麼可能會錯誤地忽略這些URI,並繞過特定於Web服務的安全措施。

這裏是堆棧溢出到同一類型的問題,這是從來沒有完全回答的引用:

Spring Boot: Configure a url prefix for RestControllers

+0

在Jira爲此打開了一張票:https://jira.spring.io/browse/SPR-13882 – pczeus

回答

1

有人在Spring MVC吉拉已經提交的問題,並提出了一個很好的解決方案,我現在正在使用它。這個想法是在放置在每個RestController文件中的前綴中使用Spring表達式語言,並引用Spring Boot application.properties文件中的單個屬性。

這是問題的鏈接:https://jira.spring.io/browse/SPR-13882

+1

感謝您發佈對Jira票Olivier的參考。我應該早些發佈它。我曾在Springs開發者論壇上提出過同樣的問題,並被提示打開該門票,這是我所做的。不知道這是一個很好的解決方案,但就足夠了。 – pczeus

+0

不幸的是,當我使用Spring HATEOAS時,現在已經失效。當我使用框架來生成鏈接時,它將拾取Spring EL並將'{'/'}'解釋爲路徑變量......它在此處打破:https://github.com/spring-projects/spring-框架/ BLOB/v4.2.4.RELEASE /彈簧網/ src目錄/主/ JAVA /組織/ springframework的/ WEB/UTIL/UriComponents.java#L327 –

3

我有同樣的擔心,是不是春天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); 
    } 
} 
14

有解決這類可用的問題,因爲春天引導1.4.0新的解決方案。RC1(詳情見https://github.com/spring-projects/spring-boot/issues/5004

Shahin ASkari的解決方案禁用部分自動配置,因此可能會導致其他問題。

以下解決方案將他的想法合併到彈簧引導中。對於我來說,我想與基本路徑API的所有RestControllers,但仍然提供靜態內容與根路徑(FE角的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); 
        } 
       }; 
      } 
     }; 
    } 

} 
3

稍微更簡潔的解決方案,其不重複檢查的註釋的邏輯,但僅改變所述映射路徑:

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中,這會打破錯誤處理(DispatcherServlet仍然會將錯誤重定向到/ error而不帶前綴!)。

可能的解決方案是在上面的代碼中添加/ api前綴(多一個「if」)時跳過/錯誤路徑。

相關問題