spring-boot - 带有 CookieLocaleResolver 的 Spring Boot 应用程序中的错误页面

标签 spring-boot jboss-eap-6

我有一个 Spring Boot 应用程序,它具有用于语言环境解析器的 org.springframework.web.servlet.i18n.CookieLocaleResolver。如果存在像 !en 这样的无效语言 cookie,那么将出现异常 java.lang.IllegalArgumentException: Locale part "!en"包含无效字符

问题是这个异常没有被 Spring Boot 处理,而是被转发到 Servlet 容器。所以显示容器的默认错误页面(在我的例子中是 JBoss EAP 6),它将显示堆栈跟踪。

来自 Controller 的其他异常已得到妥善处理。例如,我有一个 Controller 映射,它会抛出 /by zero error 并得到正确处理。

我试过在 web.xml 中配置错误页面如下。

<error-page>
    <location>/500</location>
</error-page>

并将 /error/500 映射到 MVC Controller ,如下所示。

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 在通过调度程序发送请求之前确定 Locale,因为解析 Locale 时抛出的异常不会在 processDispatchResult< 中被捕获 因此不会像普通的 WebMvc 错误那样得到处理。对于上下文,FrameworkServletDispatcherServlet 扩展,它覆盖 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);
            }
        };
    }
}

关于spring-boot - 带有 CookieLocaleResolver 的 Spring Boot 应用程序中的错误页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39061360/

相关文章:

java - 如何让 jboss 在 URL 路径中接受 %2F 编码的斜杠

jboss - 将数据源连接到 JBOSS EAP 6.4 的 Java 代码 : Naming Exception

spring-boot - 在 Spring JMS 监听器中禁用事务管理

spring - Jboss重新部署不更新jsp

java - 如何在Spring OAuth2中仅通过client_id和secret进行授权?

spring - 如何使用 spring RestTemplate 发送 multipartFile?

java - 到达本地网络中的 JBoss EAP 6 服务器

maven - JBoss EAP 6.1 上的热插拔代理 - java.lang.OutOfMemoryError : PermGen space

java - spring-boot @ConditionalOnClass 是如何工作的?

java - 将 GET 响应从 restTemplate 转换为自定义类