Spring 中的错误页面 使用 CookieLocaleResolver 引导应用程序
Error page in Spring Boot application with CookieLocaleResolver
我有一个 Spring 引导应用程序,其中包含 org.springframework.web.servlet.i18n.CookieLocaleResolver
区域设置解析器。如果有像 !en
这样的无效语言 cookie,那么就会出现异常 java.lang.IllegalArgumentException: Locale part "!en" contains invalid characters
.
问题是此异常未由 Spring 引导处理,而是转发到 Servlet 容器。因此显示了容器的默认错误页面(在我的例子中是 JBoss EAP 6),它将显示堆栈跟踪。
控制器的其他异常已得到妥善处理。例如,我有一个控制器映射,它会抛出 / by zero error
并得到正确处理。
我已经尝试在 web.xml 中配置错误页面,如下所示。
<error-page>
<location>/500</location>
</error-page>
并将 /error
和 /500
映射到 MVC 控制器,如下所示。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class CustomErrorController extends AbstractErrorController {
public static final String ERROR_500 = "/500";
private static final String ERROR_PATH= "/error";
@Autowired
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
/**
* Responsible for handling all errors and throw especial exceptions
* for some HTTP status codes. Otherwise, it will return a map that
* ultimately will be converted to a json error.
*/
@RequestMapping({ERROR_PATH,ERROR_500})
public ResponseEntity<?> handleErrors(HttpServletRequest request) {
return ResponseEntity.status(getStatus(request)).body(getErrorAttributes(request, false));
}
@Override
public String getErrorPath() {
return ERROR_PATH;
}
}
但我仍然收到容器的默认错误页面。如何解决这个问题。
处理请求的 FrameworkServlet
在通过调度程序发送请求之前确定区域设置,因为解析区域设置时抛出的异常不会在 processDispatchResult
中被捕获并且因此不会像普通的 WebMvc 错误那样得到处理。对于上下文,FrameworkServlet
由 DispatcherServlet
扩展,它覆盖 buildLocaleContext(request)
并依次调用 CookieLocaleResolver
实例。
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// Here the locale is determined
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// here is where the WebMvc processing happens
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
buildLocaleContext() 的 DispatcherServlet 方法
/**
* Build a LocaleContext for the given request, exposing the request's primary locale as current locale.
* <p>The default implementation uses the dispatcher's LocaleResolver to obtain the current locale,
* which might change during a request.
* @param request current HTTP request
* @return the corresponding LocaleContext
*/
@Override
protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
if (this.localeResolver instanceof LocaleContextResolver) {
return ((LocaleContextResolver) this.localeResolver).resolveLocaleContext(request);
}
else {
return new LocaleContext() {
@Override
public Locale getLocale() {
return localeResolver.resolveLocale(request);
}
};
}
}
我认为您还需要在 web.xml
的参数中提及 error-code
,如下所示。它对我有用。
<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>
然后在 controller
中捕获它
@RequestMapping(value = "/500", method = RequestMethod.GET)
public String error500(Model model) {
//System.out.println("Error 500 ");
return "500"; //will return the 500.jsp
}
我有一个 Spring 引导应用程序,其中包含 org.springframework.web.servlet.i18n.CookieLocaleResolver
区域设置解析器。如果有像 !en
这样的无效语言 cookie,那么就会出现异常 java.lang.IllegalArgumentException: Locale part "!en" contains invalid characters
.
问题是此异常未由 Spring 引导处理,而是转发到 Servlet 容器。因此显示了容器的默认错误页面(在我的例子中是 JBoss EAP 6),它将显示堆栈跟踪。
控制器的其他异常已得到妥善处理。例如,我有一个控制器映射,它会抛出 / by zero error
并得到正确处理。
我已经尝试在 web.xml 中配置错误页面,如下所示。
<error-page>
<location>/500</location>
</error-page>
并将 /error
和 /500
映射到 MVC 控制器,如下所示。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class CustomErrorController extends AbstractErrorController {
public static final String ERROR_500 = "/500";
private static final String ERROR_PATH= "/error";
@Autowired
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
/**
* Responsible for handling all errors and throw especial exceptions
* for some HTTP status codes. Otherwise, it will return a map that
* ultimately will be converted to a json error.
*/
@RequestMapping({ERROR_PATH,ERROR_500})
public ResponseEntity<?> handleErrors(HttpServletRequest request) {
return ResponseEntity.status(getStatus(request)).body(getErrorAttributes(request, false));
}
@Override
public String getErrorPath() {
return ERROR_PATH;
}
}
但我仍然收到容器的默认错误页面。如何解决这个问题。
处理请求的 FrameworkServlet
在通过调度程序发送请求之前确定区域设置,因为解析区域设置时抛出的异常不会在 processDispatchResult
中被捕获并且因此不会像普通的 WebMvc 错误那样得到处理。对于上下文,FrameworkServlet
由 DispatcherServlet
扩展,它覆盖 buildLocaleContext(request)
并依次调用 CookieLocaleResolver
实例。
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// Here the locale is determined
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// here is where the WebMvc processing happens
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
buildLocaleContext() 的 DispatcherServlet 方法
/**
* Build a LocaleContext for the given request, exposing the request's primary locale as current locale.
* <p>The default implementation uses the dispatcher's LocaleResolver to obtain the current locale,
* which might change during a request.
* @param request current HTTP request
* @return the corresponding LocaleContext
*/
@Override
protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
if (this.localeResolver instanceof LocaleContextResolver) {
return ((LocaleContextResolver) this.localeResolver).resolveLocaleContext(request);
}
else {
return new LocaleContext() {
@Override
public Locale getLocale() {
return localeResolver.resolveLocale(request);
}
};
}
}
我认为您还需要在 web.xml
的参数中提及 error-code
,如下所示。它对我有用。
<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>
然后在 controller
@RequestMapping(value = "/500", method = RequestMethod.GET)
public String error500(Model model) {
//System.out.println("Error 500 ");
return "500"; //will return the 500.jsp
}