spring 带有资源处理程序的启动错误页面
spring boot error page with resource handlers
tl;dr:如何为 spring 引导错误页面启用 spring 的 ResourceUrlEncodingFilter?
(使用spring boot 1.3.7.RELEASE和Spring Framework/MVC 4.2.4.RELEASE时写的问题)
一些背景:我们有一个相当标准的 spring boot/spring webmvc 项目,使用 Thymeleaf 作为视图层。我们启用了开箱即用的 spring 引导资源链来提供静态资产。
我们的 thymeleaf 视图中有标准的 url 编码语法,例如 <script th:src="@{/js/some-page.js}"></script>
。这依赖于 Spring 的 org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
将 url 转换为适当版本的 url,例如 /v1.6/js/some-page.js
.
我们的错误处理是通过以下方式完成的:
- 设置
server.error.whitelabel.enabled=false
- 子类化 spring 引导的默认值
BasicErrorController
以覆盖 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response)
- 依靠我们已经配置好的 thymeleaf 视图解析器来呈现我们的自定义错误页面
问题是:ResourceUrlEncodingFilter 没有应用于我们的错误页面。我假设缺少为 ERROR 分派请求注册的过滤器,但对我来说并不明显:a) 如何在 spring 引导中自定义它;和 b) 为什么默认情况下没有这样做。
更新 1:
问题似乎与 OncePerRequestFilter
和 ERROR 调度程序的组合有关。即:
默认情况下,ResouceUrlEncodingFilter
不绑定到 ERROR 调度程序。虽然重写这很麻烦,但这并非不可能,但由于以下原因无济于事:
OncePerRequestFilter
(ResourceUrlEncodingFilter
的父级)在请求上设置一个属性,指示它已被应用,以便不再重新应用。然后它包装响应对象。但是,当发出 ERROR 时,未 使用包装的响应,并且由于请求属性仍然存在,过滤器不会重新包装。
更糟糕的是,自定义 boolean hasAlreadyFilteredAttribute
的逻辑不能被请求覆盖。 OncePerRequestFilter
的doFilter()
方法是final,getAlreadyFilteredAttributeName()
(扩展点)没有访问当前请求对象获取dispatcher .
我觉得我一定是错过了什么;在 spring 启动时似乎无法在 404 页面上使用版本化资源。
更新 2:有效但混乱的解决方案
这是我能想到的最好的,但看起来仍然很乱:
public abstract class OncePerErrorRequestFilter extends OncePerRequestFilter {
@Override
protected String getAlreadyFilteredAttributeName() {
return super.getAlreadyFilteredAttributeName() + ".ERROR";
}
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
}
public class ErrorPageCapableResourceUrlEncodingFilter extends OncePerErrorRequestFilter {
// everything in here is a perfect copy-paste of ResourceUrlEncodingFilter since the internal ResourceUrlEncodingResponseWrapper is private
}
// register the error-supporting version if the whitelabel error page has been disabled ... could/should use a dedicated property for this
@Configuration
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
@ConditionalOnClass(OncePerErrorRequestFilter.class)
@ConditionalOnWebApplication
@ConditionalOnEnabledResourceChain
@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", havingValue="false", matchIfMissing = false)
public static class ThymeleafResourceUrlEncodingFilterErrorConfiguration {
@Bean
public FilterRegistrationBean errorPageResourceUrlEncodingFilterRegistration() {
FilterRegistrationBean reg = new FilterRegistrationBean();
reg.setFilter(new ErrorPageCapableResourceUrlEncodingFilter());
reg.setDispatcherTypes(DispatcherType.ERROR);
return reg;
}
}
更好的解决方案?
这已在 spring-projects/spring-boot#7348 中报告,并且正在修复中。
看来您已经对问题进行了广泛的分析;太糟糕了,你没有早点报告这个问题。下次,请考虑在 Spring 启动跟踪器上提出这些问题。
谢谢!
tl;dr:如何为 spring 引导错误页面启用 spring 的 ResourceUrlEncodingFilter?
(使用spring boot 1.3.7.RELEASE和Spring Framework/MVC 4.2.4.RELEASE时写的问题)
一些背景:我们有一个相当标准的 spring boot/spring webmvc 项目,使用 Thymeleaf 作为视图层。我们启用了开箱即用的 spring 引导资源链来提供静态资产。
我们的 thymeleaf 视图中有标准的 url 编码语法,例如 <script th:src="@{/js/some-page.js}"></script>
。这依赖于 Spring 的 org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
将 url 转换为适当版本的 url,例如 /v1.6/js/some-page.js
.
我们的错误处理是通过以下方式完成的:
- 设置
server.error.whitelabel.enabled=false
- 子类化 spring 引导的默认值
BasicErrorController
以覆盖public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response)
- 依靠我们已经配置好的 thymeleaf 视图解析器来呈现我们的自定义错误页面
问题是:ResourceUrlEncodingFilter 没有应用于我们的错误页面。我假设缺少为 ERROR 分派请求注册的过滤器,但对我来说并不明显:a) 如何在 spring 引导中自定义它;和 b) 为什么默认情况下没有这样做。
更新 1:
问题似乎与 OncePerRequestFilter
和 ERROR 调度程序的组合有关。即:
-
默认情况下,
ResouceUrlEncodingFilter
不绑定到 ERROR 调度程序。虽然重写这很麻烦,但这并非不可能,但由于以下原因无济于事:OncePerRequestFilter
(ResourceUrlEncodingFilter
的父级)在请求上设置一个属性,指示它已被应用,以便不再重新应用。然后它包装响应对象。但是,当发出 ERROR 时,未 使用包装的响应,并且由于请求属性仍然存在,过滤器不会重新包装。
更糟糕的是,自定义 boolean hasAlreadyFilteredAttribute
的逻辑不能被请求覆盖。 OncePerRequestFilter
的doFilter()
方法是final,getAlreadyFilteredAttributeName()
(扩展点)没有访问当前请求对象获取dispatcher .
我觉得我一定是错过了什么;在 spring 启动时似乎无法在 404 页面上使用版本化资源。
更新 2:有效但混乱的解决方案
这是我能想到的最好的,但看起来仍然很乱:
public abstract class OncePerErrorRequestFilter extends OncePerRequestFilter {
@Override
protected String getAlreadyFilteredAttributeName() {
return super.getAlreadyFilteredAttributeName() + ".ERROR";
}
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
}
public class ErrorPageCapableResourceUrlEncodingFilter extends OncePerErrorRequestFilter {
// everything in here is a perfect copy-paste of ResourceUrlEncodingFilter since the internal ResourceUrlEncodingResponseWrapper is private
}
// register the error-supporting version if the whitelabel error page has been disabled ... could/should use a dedicated property for this
@Configuration
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
@ConditionalOnClass(OncePerErrorRequestFilter.class)
@ConditionalOnWebApplication
@ConditionalOnEnabledResourceChain
@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", havingValue="false", matchIfMissing = false)
public static class ThymeleafResourceUrlEncodingFilterErrorConfiguration {
@Bean
public FilterRegistrationBean errorPageResourceUrlEncodingFilterRegistration() {
FilterRegistrationBean reg = new FilterRegistrationBean();
reg.setFilter(new ErrorPageCapableResourceUrlEncodingFilter());
reg.setDispatcherTypes(DispatcherType.ERROR);
return reg;
}
}
更好的解决方案?
这已在 spring-projects/spring-boot#7348 中报告,并且正在修复中。
看来您已经对问题进行了广泛的分析;太糟糕了,你没有早点报告这个问题。下次,请考虑在 Spring 启动跟踪器上提出这些问题。
谢谢!