`
chenlk823
  • 浏览: 37358 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

自定义 Tomcat 404错误页面

 
阅读更多
Tomcat 的错误页面是由 org.apache.catalina.valves.ErrorReportValve 类输出来的。如果想自定义错误页面,不需要修改该类。Servlet 规范声明了相关的API,只需要在每个 web 应用的 web.xml 里定义。可按照错误类型、错误代码配置。例如:

<web-app 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-app_2_5.xsd"
   version="2.5">

<display-name>Welcome to Tomcat</display-name>
<description>
     Welcome to Tomcat
</description>

<error-page>
<error-code>404</error-code>
<location>/errorpages/404.jsp</location>
</error-page>  

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

</web-app>

注意错误页面必须以“/”开头,这样任何path的404错误页面及exception错误都会映射到这两个文件。然后在本web引用的errorpages下面放置404.jsp, exception.jsp两个文件。

错误页面 404.jsp:

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.*" %>
<html>
<header>
<title>404 page</title>
<body>

<pre>
<%
    Enumeration<String> attributeNames = request.getAttributeNames();
    while (attributeNames.hasMoreElements())
    {
        String attributeName = attributeNames.nextElement();
        Object attribute = request.getAttribute(attributeName);
   out.println("request.attribute['" + attributeName + "'] = " + attribute);
    }
%>
</pre>

代码中输出了所有的 request 中的变量。从中也可以看到访问哪个文件出错,跳到哪个错误页面了,从而进行更详细、更人性化的错误处理。例如,提示可能的正确网址等等。

例如:访问一个不存在的页面 page_not_exist.html,显示的信息为:

request.attribute['javax.servlet.forward.request_uri'] = /page_not_exists.html
request.attribute['javax.servlet.forward.context_path'] =
request.attribute['javax.servlet.forward.servlet_path'] = /page_not_exists.html
request.attribute['javax.servlet.forward.path_info'] = /errorpages/404.jsp
request.attribute['javax.servlet.error.message'] = /page_not_exists.html
request.attribute['javax.servlet.error.status_code'] = 404
request.attribute['javax.servlet.error.servlet_name'] = default
request.attribute['javax.servlet.error.request_uri'] = /page_not_exists.html

注意,该错误页面必须大于512字节,否则IE将不予显示。因为IE默认只显示大于512字节的错误页面。Firefox中正常显示。可以添加一些其他信息,将页面大小扩充到512字节以上。如果仍不能显示,请检查IE设置,将该选项选中。




异常处理页面 exception.jsp:

<%@ page contentType="text/html; charset=UTF-8" isErrorPage="true" %>
<%@ page import="java.io.*" %>
<html>
<header>
<title>exception page</title>
<body>
<hr/>
<pre>
<%
response.getWriter().println("Exception: " + exception);

if(exception != null)
{
   response.getWriter().println("<pre>");
   exception.printStackTrace(response.getWriter());
   response.getWriter().println("</pre>");
}

response.getWriter().println("<hr/>");
%>

注意isErrorPage熟悉必须为true,才能使用exception对象。exception即捕捉到的异常。此处可以对exception进行处理,比如记录日志、重定向等等。这里把exception trace打印出来了。

500、505 等错误页面的处理类似于404。

----------------------------------------------------------------------------------

还有一种方法是,自定义ErrorReportValve。适合context比较多的情况。自己实现一个ErrorReportValve,就可以适用于所有的404、505、500等错误了。实现后需打成 jar 包,放置到 tomcat 的全局lib里,并配置到tomcat 的server.xml里:

      <Host name="localhost" appBase="webapps"

            unpackWARs="true" autoDeploy="true" errorReportValveClass="com.helloweenpad.xxxxx.OurCustomizedErrorReportValve"

            xmlValidation="false" xmlNamespaceAware="false">

相关 tomcat 的文档为:

errorReportValveClass
Java class name of the error reporting valve which will be used by this Host. The responsability of this valve is to output error reports. Setting this property allows to customize the look of the error pages which will be generated by Tomcat. This class must implement the org.apache.catalina.Valve interface. If none is specified, the value org.apache.catalina.valves.ErrorReportValve will be used by default.



实现方式见 Tomcat 的 org.apache.catalina.valves.ErrorReportValve 类:




/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package org.apache.catalina.valves;


import java.io.IOException;
import java.io.Writer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Globals;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.RequestUtil;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.StringManager;

/**
* <p>Implementation of a Valve that outputs HTML error pages.</p>
*
* <p>This Valve should be attached at the Host level, although it will work
* if attached to a Context.</p>
*
* <p>HTML code from the Cocoon 2 project.</p>
*
* @author Remy Maucherat
* @author Craig R. McClanahan
* @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
* @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
* @author Yoav Shapira
* @version $Revision: 543307 $ $Date: 2007-06-01 01:08:24 +0200 (Fri, 01 Jun 2007) $
*/

public class ErrorReportValve
    extends ValveBase {


    // ----------------------------------------------------- Instance Variables


    /**
     * The descriptive information related to this implementation.
     */
    private static final String info =
        "org.apache.catalina.valves.ErrorReportValve/1.0";


    /**
     * The StringManager for this package.
     */
    protected static StringManager sm =
        StringManager.getManager(Constants.Package);


    // ------------------------------------------------------------- Properties


    /**
     * Return descriptive information about this Valve implementation.
     */
    public String getInfo() {

        return (info);

    }


    // --------------------------------------------------------- Public Methods


    /**
     * Invoke the next Valve in the sequence. When the invoke returns, check
     * the response state, and output an error report is necessary.
     *
     * @param request The servlet request to be processed
     * @param response The servlet response to be created
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    public void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Perform the request
        getNext().invoke(request, response);

        Throwable throwable =
            (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);

        if (response.isCommitted()) {
            return;
        }

        if (throwable != null) {

            // The response is an error
            response.setError();

            // Reset the response (if possible)
            try {
                response.reset();
            } catch (IllegalStateException e) {
                ;
            }

            response.sendError
                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

        }

        response.setSuspended(false);

        try {
            report(request, response, throwable);
        } catch (Throwable tt) {
            ;
        }

    }


    // ------------------------------------------------------ Protected Methods


    /**
     * Prints out an error report.
     *
     * @param request The request being processed
     * @param response The response being generated
     * @param throwable The exception that occurred (which possibly wraps
     * a root cause exception
     */
    protected void report(Request request, Response response,
                          Throwable throwable) {

        // Do nothing on non-HTTP responses
        int statusCode = response.getStatus();

        // Do nothing on a 1xx, 2xx and 3xx status
        // Do nothing if anything has been written already
        if ((statusCode < 400) || (response.getContentCount() > 0))
            return;

        String message = RequestUtil.filter(response.getMessage());
        if (message == null)
            message = "";

        // Do nothing if there is no report for the specified status code
        String report = null;
        try {
            report = sm.getString("http." + statusCode, message);
        } catch (Throwable t) {
            ;
        }
        if (report == null)
            return;

        StringBuffer sb = new StringBuffer();

        sb.append("<html><head><title>");
        sb.append(ServerInfo.getServerInfo()).append(" - ");
        sb.append(sm.getString("errorReportValve.errorReport"));
        sb.append("</title>");
        sb.append("<style><!--");
        sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
        sb.append("--></style> ");
        sb.append("</head><body>");
        sb.append("<h1>");
        sb.append(sm.getString("errorReportValve.statusHeader",
                               "" + statusCode, message)).append("</h1>");
        sb.append("<HR size=\"1\" noshade=\"noshade\">");
        sb.append("<p><b>type</b> ");
        if (throwable != null) {
            sb.append(sm.getString("errorReportValve.exceptionReport"));
        } else {
            sb.append(sm.getString("errorReportValve.statusReport"));
        }
        sb.append("</p>");
        sb.append("<p><b>");
        sb.append(sm.getString("errorReportValve.message"));
        sb.append("</b> <u>");
        sb.append(message).append("</u></p>");
        sb.append("<p><b>");
        sb.append(sm.getString("errorReportValve.description"));
        sb.append("</b> <u>");
        sb.append(report);
        sb.append("</u></p>");

        if (throwable != null) {

            String stackTrace = getPartialServletStackTrace(throwable);
            sb.append("<p><b>");
            sb.append(sm.getString("errorReportValve.exception"));
            sb.append("</b> <pre>");
            sb.append(RequestUtil.filter(stackTrace));
            sb.append("</pre></p>");

            int loops = 0;
            Throwable rootCause = throwable.getCause();
            while (rootCause != null && (loops < 10)) {
                stackTrace = getPartialServletStackTrace(rootCause);
                sb.append("<p><b>");
                sb.append(sm.getString("errorReportValve.rootCause"));
                sb.append("</b> <pre>");
                sb.append(RequestUtil.filter(stackTrace));
                sb.append("</pre></p>");
                // In case root cause is somehow heavily nested
                rootCause = rootCause.getCause();
                loops++;
            }

            sb.append("<p><b>");
            sb.append(sm.getString("errorReportValve.note"));
            sb.append("</b> <u>");
            sb.append(sm.getString("errorReportValve.rootCauseInLogs",
                                   ServerInfo.getServerInfo()));
            sb.append("</u></p>");

        }

        sb.append("<HR size=\"1\" noshade=\"noshade\">");
        sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
        sb.append("</body></html>");

        try {
            try {
                response.setContentType("text/html");
                response.setCharacterEncoding("utf-8");
            } catch (Throwable t) {
                if (container.getLogger().isDebugEnabled())
                    container.getLogger().debug("status.setContentType", t);
            }
            Writer writer = response.getReporter();
            if (writer != null) {
                // If writer is null, it's an indication that the response has
                // been hard committed already, which should never happen
                writer.write(sb.toString());
            }
        } catch (IOException e) {
            ;
        } catch (IllegalStateException e) {
            ;
        }
       
    }


    /**
     * Print out a partial servlet stack trace (truncating at the last
     * occurrence of javax.servlet.).
     */
    protected String getPartialServletStackTrace(Throwable t) {
        StringBuffer trace = new StringBuffer();
        trace.append(t.toString()).append('\n');
        StackTraceElement[] elements = t.getStackTrace();
        int pos = elements.length;
        for (int i = 0; i < elements.length; i++) {
            if ((elements[i].getClassName().startsWith
                 ("org.apache.catalina.core.ApplicationFilterChain"))
                && (elements[i].getMethodName().equals("internalDoFilter"))) {
                pos = i;
            }
        }
        for (int i = 0; i < pos; i++) {
            if (!(elements[i].getClassName().startsWith
                  ("org.apache.catalina.core."))) {
                trace.append('\t').append(elements[i].toString()).append('\n');
            }
        }
        return trace.toString();
    }

}



分享到:
评论

相关推荐

    在Tomcat中配置404自定义错误页面详解

    主要介绍了在Tomcat中配置404自定义错误页面全解,需要的朋友可以参考下

    404,500错误页面设计

    例如,在Tomcat服务器中,可以在`web.xml`配置文件中添加如下代码来设置404和500错误页面: ```xml &lt;error-code&gt;404 &lt;location&gt;/404.htm&lt;/location&gt; &lt;error-code&gt;500 &lt;location&gt;/500.html&lt;/location&gt; ``` ...

    web工程中404/500错误页面配置+404页面模板

    在Tomcat等Java Web服务器中,我们需要在`web.xml`部署描述符中配置错误页面: ```xml &lt;error-code&gt;404 &lt;location&gt;/error/404.jsp &lt;error-code&gt;500 &lt;location&gt;/error/500.jsp ``` 这里的`/error/404.jsp`和...

    struts2 跳转至404 页面的解决方案

    检查`struts-default.xml`或自定义的配置文件,确认404错误是否被正确地映射到了一个自定义的错误页面。 8. **IDE或服务器设置**:有时候,开发环境或服务器的配置也可能导致404错误,例如Tomcat的Context配置、...

    struts2完全捕获404错误的方法

    这里,`custom404`是你自定义的拦截器,`defaultStack`是Struts2的默认拦截器栈,`/error/404.jsp`是404错误页面的路径。 3. **拦截器堆栈**: 将自定义拦截器添加到默认的拦截器堆栈中,或者创建一个新的拦截器...

    Tomcat7.0 tomcat最新版本

    9. **错误处理**:Tomcat 7.0改进了错误页面处理,可以自定义错误页面并提供更友好的错误报告,便于开发者调试。 10. **国际化支持**:Tomcat 7.0增强了对多语言环境的支持,使得全球化的Web应用部署更为方便。 ...

    jsp(Tomcat)中errorPage不能使用的原因解决方法,Jsp技术文章,JSP系列教程,Jsp.htm.rar

    如果路径不正确,Tomcat将找不到错误页面。 3. **权限问题**:错误页面的访问权限可能不足。确保错误页面文件具有足够的读取和执行权限,以便Tomcat可以正确加载。 4. **过滤器冲突**:如果在应用中使用了过滤器,...

    tomcat异常配置

    然而,为了提供更友好的用户体验,我们通常希望自定义这些错误页面,将其设计成包含更详细信息,或者与网站整体风格一致的页面。 1. **错误页面配置**: - 在Tomcat的`conf/web.xml`文件中,你可以找到并编辑`...

    tomcat源码

    7. **异常处理**:Tomcat的错误页面和异常处理机制,以及如何自定义错误页面。 8. **性能优化**:通过调整Tomcat的配置参数,如最大连接数、线程池大小、缓冲区大小等,可以显著提升应用性能。 9. **JMX监控**:...

    tomcat6的源码

    10. **错误处理**:当发生错误时,Tomcat会生成定制的错误页面。源码中,`ErrorReportValve`类处理错误报告的生成。 通过分析这个源码包,开发者不仅可以了解Tomcat的工作原理,还能学习到Java服务器编程的最佳实践...

    Tomcat 5.5 Tomcat 6.0 安装版本

    6. **错误页面处理**:对于HTTP错误代码,可以自定义响应的错误页面,提升用户体验。 7. **Web应用部署**:支持通过`Context`元素在`server.xml`中定义部署,也可以使用`appBase`属性在特定目录下自动部署应用。 8...

    tomcat8和tomcat7

    4. **更好的错误处理**:提供了自定义错误页面和更详细的错误信息,便于调试。 5. **安全增强**:支持JAAS集成,增强了安全管理,如角色认证和授权。 **Tomcat8** Tomcat8于2013年发布,主要支持Java EE 7规范,...

    apache-tomcat-7.0.85.tar.gz

    可以自定义错误页面,通过在`web.xml`中配置`error-page`元素,实现特定错误代码的定制响应。 11. **JSP与Servlet** Tomcat作为Servlet容器,支持JSP的编译和执行。开发者可以创建`.jsp`文件,Tomcat会在首次请求...

    apache-tomcat-8.5

    - 错误页面可通过在WEB-INF/web.xml中定义自定义的错误处理页面来定制。 6. **安全管理**: - 可以通过conf/context.xml或WEB-INF/web.xml配置文件进行权限控制,例如限制访问特定的URL、设置角色和权限等。 - ...

    windows安装版-tomcat8.5.57.zip

    8. **错误页面**:可以自定义“conf/web.xml”中的错误页面,当应用抛出特定错误时,显示定制的错误信息。 9. **JNDI资源**:Tomcat提供了JNDI(Java Naming and Directory Interface)服务,允许在服务器中注册和...

    tomcat的概要介绍与分析

    为了增加网站的趣味性和个性化元素,Tomcat允许用户通过简单的配置来展示特别设计的404错误页面。这种做法不仅能提升用户体验,还能让访问者在遇到页面未找到的情况下感受到网站的独特魅力。下面详细介绍如何实现这...

    apache-tomcat-6.0.44

    10. **错误与异常处理**:Tomcat默认返回详细的错误页面,但在生产环境中,通常会配置为返回自定义错误页面以提供更好的用户体验。 11. **日志管理**:Tomcat的日志系统记录了服务器运行过程中的各种信息,可以通过...

    window x64 Tomcat 8.5.27

    10. **错误页面处理**:当发生错误时,Tomcat可以根据配置返回自定义的错误页面,提供更好的用户体验。 在使用Window x64 Tomcat 8.5.27时,你需要安装Java Development Kit (JDK) 的64位版本,因为Tomcat需要JDK来...

Global site tag (gtag.js) - Google Analytics