1 問題描述
項目前端模板使用Thymeleaf,在對各種URL進行格式化輸出時,都使用@{uri}代碼。它會自動讀取項目部署的虛擬路徑,添加到URI的前端輸出。
真實測試和生產環境中,我們使用nginx+Tomcat的部署模式,這就會部署帶來一個限制:ngxin配置proxy時,需要同后端application使用相同的context path。
一個比較典型的測試場景:同一個Tomcat,部署多個應用;同一個nginx,配置這三個應用的proxy,但要求都使用獨立域名進行訪問,不能添加 context path。如圖:
2 Thymeleaf實現原理
仔細讀Thymeleaf的源碼,它對uri的封裝,是通過 LinkBuilder類實現的。在SpringBoot項目中,相關的代碼。
有幾點需要注意:
1、 最終是在 StandardLinkBuilder中調用request.getContextPath()獲取部署context.
2、 SpringTemplateEngine的構造函數中,直接new StandardLinkBuilder對象。
3、 ThymeleafAutoConfiguration的代碼和相應的配置定義中,沒有發現對LinkBuilder的配置參數。
3 解決方案
根據項目情況,可以有幾個解決方案可供選擇。
3.1 Filter + HttpServletRequestWrapper
思路:最終代碼使用request.getContextPath(),我們只要重新封裝一下Request,重寫getContextPath()方法即可。
並在項目中添加一個Filer,核心代碼為:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { CustomContextPathRequest requestWrapper = new CustomContextPathRequest( (HttpServletRequest) request, this.contextPath); filterChain.doFilter(requestWrapper, response); } |
3.2 擴展 AutoConfiguration
咱重點介紹一下這個方法,借此機會熟悉SpringBoot的機制。
思路:SpringBoot缺省的AutoConfiguration沒有提供配置LinkBuilder,我們自己實現一個AutoConfiguration,在Spring完成SpringTemplateEngine成功之后,再替換器LinkBuilder實現。
3.2.1 ManualContextLinkBuilder
例子是將context path寫死為 /demo ,實際代碼中,可以通過在application.propertis中的變量來實現,並配合maven profile,實現不同運行環境的差異化實現。
public class ManualContextLinkBuilder extends StandardLinkBuilder { private String nginxContextPath = “/demo”;
@Override protected String computeContextPath(final IExpressionContext context, final String base, final Map<String, Object> parameters) { return nginxContextPath; } } |
3.2.2 ManualContextLinkBuilderConfiguration
@Configuration @AutoConfigureAfter(WebMvcAutoConfiguration.class) public class ManualContextLinkBuilderConfiguration { @Autowired SpringTemplateEngine springTemplateEngine;
@Bean public ILinkBuilder linkBuilder() { ILinkBuilder linkBuilder = new ManualContextLinkBuilder(); springTemplateEngine.setLinkBuilder(linkBuilder); return linkBuilder; } } |
3.2.3 META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ tech.codestory.ManualContextLinkBuilderConfiguration |