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
来源: http://www.92to.com/bangong/2018/02-10/33307605.html