Каков хороший подход к отправке исключения из сервлетов на страницу jsp?

Я знаю, что могу поместить что-то в web.xml, как это

<error-page>  
   <exception-type>java.lang.Throwable</exception-type>  
   <location>/error.jsp</location>  
</error-page>

Однако на странице jsp не будет отображаться какая-либо компрометирующая информация, так как она не получит то, что именно является исключением. Я знаю, что у нас могут быть разные исключения, отправленные на разные страницы различными exception-type, но это слишком много для написания в web.xml. Я надеюсь, что одной страницы достаточно, а другой для обработки ошибок, таких как 404.

Итак, как мне передать информацию об исключениях на страницу jsp? Использовать сеанс?

Идеальная ситуация может заключаться в том, что страница получает информацию об исключении и показывает некоторые соответствующие сообщения об этом, не раскрывая исключения пользователям. Вместо этого он может записать его в файл для дальнейшего использования. Каков наилучший подход для достижения этого? Спасибо.

Ответ 1

Информация об исключении составляет уже, доступная несколькими атрибутами запроса. Вы можете найти имена всех этих атрибутов в RequestDispatcher javadoc:

Итак, вкратце, этот пример JSP должен отображать все возможные детали исключения:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
<ul>
    <li>Exception: <c:out value="${requestScope['javax.servlet.error.exception']}" /></li>
    <li>Exception type: <c:out value="${requestScope['javax.servlet.error.exception_type']}" /></li>
    <li>Exception message: <c:out value="${requestScope['javax.servlet.error.message']}" /></li>
    <li>Request URI: <c:out value="${requestScope['javax.servlet.error.request_uri']}" /></li>
    <li>Servlet name: <c:out value="${requestScope['javax.servlet.error.servlet_name']}" /></li>
    <li>Status code: <c:out value="${requestScope['javax.servlet.error.status_code']}" /></li>
</ul>

Кроме того, вы также можете показать эту полезную информацию:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<jsp:useBean id="date" class="java.util.Date" />
...
<ul>
    <li>Timestamp: <fmt:formatDate value="${date}" type="both" dateStyle="long" timeStyle="long" /></li>
    <li>User agent: <c:out value="${header['user-agent']}" /></li>
</ul>

Конкретный экземпляр Exception сам по себе находится в JSP, доступном только как ${exception}, когда вы отмечаете страницу как страницу с ошибкой:

<%@ page isErrorPage="true" %>
...
${exception}

Только если вы используете EL 2.2 или новее, вы можете распечатать его стек, как показано ниже:

<%@ page isErrorPage="true" %>
...
<pre>${pageContext.out.flush()}${exception.printStackTrace(pageContext.response.writer)}</pre>

Или, если вы еще не на EL 2.2, создайте пользовательскую функцию EL:

public final class Functions {

    private Functions() {}

    public static String printStackTrace(Throwable exception) {
        StringWriter stringWriter = new StringWriter();
        exception.printStackTrace(new PrintWriter(stringWriter, true));
        return stringWriter.toString();
    }

}

Что зарегистрировано в /WEB-INF/functions.tld:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib 
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">

    <display-name>Custom Functions</display-name>    
    <tlib-version>1.0</tlib-version>
    <uri>http://example.com/functions</uri>

    <function>
        <name>printStackTrace</name>
        <function-class>com.example.Functions</function-class>
        <function-signature>java.lang.String printStackTrace(java.lang.Throwable)</function-signature>
    </function>
</taglib>

И может использоваться как

<%@ taglib prefix="my" uri="http://example.com/functions" %>
...
<pre>${my:printStackTrace(exception)}</pre>

Что касается регистрации исключения, самым простым местом будет filter, который отображается в шаблоне URL-адреса /* и выполняет в основном следующее:

try {
    chain.doFilter(request, response);
} catch (ServletException e) {
    log(e.getRootCause());
    throw e;
} catch (IOException e) { // If necessary? Usually not thrown by business code.
    log(e);
    throw e;
}

Ответ 2

да. По моему мнению, сеанс - это хорошее место для хранения исключений, связанных с текущим запросом.

Не забудьте очистить исключение после того, как вы его обработаете.

Кроме того, вы можете передать вместо кода исключения код ошибки из вашего кода поддержки на ваш уровень представления, где это можно перевести с использованием файлов свойств на какой-то смысл полной ошибки для пользователя.