Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/399.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在不指定servlet位置的情况下隐瞒错误代码?_Java_Tomcat_Http Status Codes - Fatal编程技术网

Java 如何在不指定servlet位置的情况下隐瞒错误代码?

Java 如何在不指定servlet位置的情况下隐瞒错误代码?,java,tomcat,http-status-codes,Java,Tomcat,Http Status Codes,在Tomcat中,我们可以在web应用程序的web.xml中指定如下片段,以定义自定义错误处理程序: <error-page> <error-code>500</error-code> <location>/SomeServlet</location> </error-page> <error-page> <location>/SomeServlet</location

在Tomcat中,我们可以在web应用程序的
web.xml
中指定如下片段,以定义自定义错误处理程序:

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

500
/SomeServlet
还可以使用一个处理程序覆盖所有错误代码的内置页面:

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

/SomeServlet
正如我们所看到的,当我们这样做时,总是有一个自定义处理程序;在上面的片段中,它是
/SomeServlet

但是我在想,如果我只是想对另一个现有的内置处理程序的特定错误代码撒谎,例如,撒谎说一个
http404
是一个
http401
,然后用
http401
的内置处理程序进行响应,那该怎么办呢


我怎么能做到这一点,甚至可能做到这一点?如果不是,使用Tomcat的内置库隐瞒错误代码的最佳实践是什么?

直接转发到错误页面而不是错误处理程序。也许您可以访问以下内容:

<error-code>500</error-code>
<location>/resources/errorPage404.jsp</location>
500
/参考资料/errorPage404.jsp

如果使用Spring,答案肯定就在那里,而不是Tomcat。这是一个很大的假设。我看不到Tomcat能做那样的事。然而,这并不完全是零代码

通常,处理web请求时引发的任何未经处理的异常都会导致服务器返回HTTP 500响应。但是,您自己编写的任何异常都可以使用@ResponseStatus注释进行注释(它支持HTTP规范定义的所有HTTP状态代码)。当从控制器方法抛出带注释的异常,而不是在其他地方处理时,它将自动导致使用指定的状态代码返回相应的HTTP响应

实现你自己的并使用它。通过这种方式,您可以为一个代码设置
,并在HTTP响应中为另一个代码返回响应

另一种方法是实现您自己的,但这会对性能产生重大影响


用谷歌举例说明如何实现它。

这一个看起来很有趣,所以我试了一下

遗憾的是,我找不到一个好方法来实现这一点,只使用配置。但是,我确实想出了一种方法,允许您在web.xml中设置一个筛选器,并通过该筛选器的init参数“重新指定”所有HTTP状态代码

需要注意的是,使这个答案与其他答案不同的是,我确实实现了一个过滤器和一个HttpServletResponseWrapper,因为我想捕获所有角落的情况,包括响应在过滤器接收响应之前的情况。这种提交可以在不同的地方发生,例如客户端代码调用、框架等

无论如何,使用web.xml片段时,会出现如下情况:

<filter>
    <filter-name>HttpStatusCodeFilter</filter-name>
    <filter-class>net.stackoverflow.HttpStatusCodeConverter</filter-class>
    <init-param>
        <param-name>502</param-name>
        <param-value>403</param-value>
    </init-param>
    <init-param>
        <param-name>405</param-name>
        <param-value>403</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>HttpStatusCodeFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

HttpStatusCodeFilter
net.stackoverflow.HttpStatusCodeConverter
502
403
405
403
HttpStatusCodeFilter
/*
您的Servlet过滤器(以及内部HttpServletResponseWrapper类)如下所示:

public class HttpStatusCodeConverter implements Filter {

    private static final Logger logger
            = Logger.getLogger(HttpStatusCodeConverter.class.getName());
    private Map<Integer, Integer> errorMap;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        if (response instanceof HttpServletResponse) {
            AntiCommitResponseWrapper hr = new AntiCommitResponseWrapper(
                    (HttpServletResponse) response);

            //pre-filter check for error codes, if code exists, no need to continue
            if (!hasFilterCode(hr)) {
                //no error yet, progress through filters
                try {
                    chain.doFilter(request, hr);
                } catch (Throwable t) {
                    if (!hasFilterCode(hr)) {
                        //exception from a filter but no code was set, set it now
                        hr.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    }
                }
            }

            //do post-filter check for error codes
            if (hasFilterCode(hr)) {
                hr.sendError(errorMap.get(hr.getStatus()));
            }

            hr.complete();
        }

    }

    private boolean hasFilterCode(HttpServletResponse hr) {
        return errorMap != null && errorMap.containsKey(hr.getStatus());
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig filterConfig) {
        errorMap = new HashMap<>();
        Enumeration<String> paramNames = filterConfig.getInitParameterNames();
        while (paramNames.hasMoreElements()) {
            String name = paramNames.nextElement();
            try {
                errorMap.put(Integer.valueOf(name),
                        Integer.valueOf(filterConfig.getInitParameter(name)));
            } catch (NumberFormatException ex) {
                logger.log(Level.WARNING, "Invalid HTTP status code mapping "
                        + "''{0}''->''{1}''.", 
                        new Object[]{name, filterConfig.getInitParameter(name)});
            }
        }
    }

    private class AntiCommitResponseWrapper extends HttpServletResponseWrapper {

        private int status = SC_OK;
        private String statusMsg;
        private String redirectLocation;
        private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();

        public AntiCommitResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public int getStatus() {
            return status;
        }

        @Override
        public void setStatus(int sc) {
            this.status = sc;
        }

        @Override
        public void sendRedirect(String location) throws IOException {
            this.status = SC_FOUND;
            this.redirectLocation = location;
        }

        @Override
        public void sendError(int sc) throws IOException {
            this.status = sc;
        }

        @Override
        public void sendError(int sc, String msg) throws IOException {
            this.status = sc;
            this.statusMsg = msg;
        }

        @Override
        public void resetBuffer() {
            buffer.reset();
        }

        @Override
        public void reset() {
            buffer.reset();
            status = SC_OK;
            statusMsg = null;
            super.reset();
        }

        @Override
        public void flushBuffer() throws IOException {
        }

        @Override
        public void setBufferSize(int size) {
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return new ServletOutputStream() {

                @Override
                public boolean isReady() {
                    return true;
                }

                @Override
                public void setWriteListener(WriteListener writeListener) {
                    try {
                        writeListener.onWritePossible();
                    } catch (IOException ex) {
                    }
                }

                @Override
                public void write(int i) throws IOException {
                    buffer.write(i);
                }
            };
        }

        /**
         * Send everything to the client.
         */
        private void complete() throws IOException {
            if (status != SC_OK) {
                super.sendError(status, statusMsg);
            } else if (status == SC_FOUND) {
                super.sendRedirect(redirectLocation);
            } else {
                super.setStatus(status);
                try (OutputStream out = super.getOutputStream()) {
                    out.write(buffer.toByteArray());
                }
            }

        }
    }
}
公共类HttpStatusCodeConverter实现过滤器{
专用静态最终记录器
=Logger.getLogger(HttpStatusCodeConverter.class.getName());
私人地图;
@凌驾
public void doFilter(ServletRequest请求、ServletResponse响应、,
过滤链(链条)
抛出IOException、ServletException{
if(HttpServletResponse的响应实例){
AntiCommitResponseWrapper hr=新的AntiCommitResponseWrapper(
(HttpServletResponse)响应);
//预筛选检查错误代码,如果代码存在,无需继续
如果(!hasFilterCode(hr)){
//还没有错误,正在通过筛选器进行处理
试一试{
链式过滤器(请求、人力资源);
}捕获(可丢弃的t){
如果(!hasFilterCode(hr)){
//筛选器异常,但未设置代码,请立即设置
hr.sendError(HttpServletResponse.SC\u内部\u服务器\u错误);
}
}
}
//是否执行过滤器后检查错误代码
if(hasFilterCode(hr)){
hr.sendError(errorMap.get(hr.getStatus());
}
hr.complete();
}
}
私有布尔hasFilterCode(HttpServletResponse hr){
return errorMap!=null&&errorMap.containsKey(hr.getStatus());
}
@凌驾
公共空间销毁(){
}
@凌驾
public void init(FilterConfig FilterConfig){
errorMap=新的HashMap();
枚举paramNames=filterConfig.getInitParameterNames();
while(paramNames.hasMoreElements()){
字符串名称=paramNames.nextElement();
试一试{
errorMap.put(整数.valueOf(名称),
Integer.valueOf(filterConfig.getInitParameter(name));
}捕获(NumberFormatException ex){
logger.log(Level.WARNING,“无效的HTTP状态代码映射”
+ "''{0}''->''{1}''.", 
新对象[]{name,filterConfig.getInitParameter(name)});
}
}
}
私有类AntiCommitResponseWrapper扩展了HttpServletResponseWrapper{
专用int状态=SC_正常;
私有字符串statusMsg;
私有字符串重定向位置;
私有最终ByteArrayOutputStream缓冲区=新建ByteArrayOutputStream();
公共反承诺响应程序(HttpServletResponse){
超级(响应);
}
@凌驾
public int getStatus(){
返回状态;
}
@凌驾
公共状态(内部sc){
这个.status=sc;
}
@凌驾
公共void sendRedirect(字符串位置)引发IOException{
this.status=SC_FOUN