Response.sendError()和setStatus()方法的区别

1.干啥的

首先我们知道setStatus()方法可以设置请求的状态值,对就是跟在返回头信息第一行HTTP版本后边的那个数字,如下图

image

sendError()方法也有这个功能,从它的重载方法来看它来可以带个错误描述的参数
image

他们都可以改变返回体的状态值

2.不同处

先说结果sendError()的状态值可以在web.xml中配置的<error-page></error-page>再次处理一遍,比如返回特定的友好的错误提示页面或者是返回具体的错误返回内容都是可以的,我觉得这个主要是对返回的body内容进行填充,当然在此也是可以改变状态码的,下边会说到。
<error-page>标签如下

<error-page>
        <error-code>401</error-code>
        <location>/noPermissionError.htm</location>
</error-page>

这里的意思是如果状态码是401的话,会再次请求/noPermissionError.htm这个请求,在此期间可以对返回内容再次调整。除了状态码外,还可以对具体的异常类型进行处理,这里就不解释了

3.为啥sendError()可以有特权

从代码层次来debug一下
首先我们来到sendError()方法处

image

这是一个权限拦截器中的一个代码,如果没有权限话的进行提示并终止调用,项目中用到了sitemesh进行返回页面的封装,所以Response是被包装过的,但是发现方法进去之后好多都不能打断点,观察Variables窗口的httpServletResponse变量可以发现最终调用的Response对象为
org.apache.catalina.connector.ResponseFacade类,这个类是tomcat-catalina这个包里的

image

进入方法


image

首先判断有没有提交,提交了直接报错
然后调用了org.apache.catalina.connector.Response类中的sendError()方法

image

注意这个sendError()这个方法
image

使用cas方法进行对一个状态值errorState设置为1 ,正是这状态值让后续的异常处理决定是否再次调用<error-page>的配置进行处理,再看下setStatus()方法一目了然
image

4.再进一步

<error-page>标签如何被解析

进入catalina包的WebXml类的configureContext()我们猜测(为啥子是猜测,因为我就是猜的哈)这个就是tomcat解析web.xml的类,然后可以进入StandardContext类的addErrorPage()方法,如下

@Override
    public void addErrorPage(ErrorPage errorPage) {
        // Validate the input parameters
        if (errorPage == null)
            throw new IllegalArgumentException
                (sm.getString("standardContext.errorPage.required"));
        String location = errorPage.getLocation();
        if ((location != null) && !location.startsWith("/")) {
            if (isServlet22()) {
                if(log.isDebugEnabled())
                    log.debug(sm.getString("standardContext.errorPage.warning",
                                 location));
                errorPage.setLocation("/" + location);
            } else {
                throw new IllegalArgumentException
                    (sm.getString("standardContext.errorPage.error",
                                  location));
            }
        }

        // Add the specified error page to our internal collections
        String exceptionType = errorPage.getExceptionType();
        if (exceptionType != null) {
            synchronized (exceptionPages) {
                exceptionPages.put(exceptionType, errorPage);
            }
        } else {
            synchronized (statusPages) {
                if (errorPage.getErrorCode() == 200) {
                    this.okErrorPage = errorPage;
                }
                statusPages.put(Integer.valueOf(errorPage.getErrorCode()),
                                errorPage);
            }
        }
        fireContainerEvent("addErrorPage", errorPage);

    }

可以清楚的看到根据异常和状态值的异常处理的整理,再猜一下,搜一下对这个statusPages的调用

就它了也是在StandardContext

@Override
    public ErrorPage findErrorPage(int errorCode) {
        if (errorCode == 200) {
            return (okErrorPage);
        } else {
            return (statusPages.get(Integer.valueOf(errorCode)));
        }

    }

查下这个方法的调用 进入StandardHostValvestatus(Request request, Response response)方法

image

这个方法上的注释已经说的很清楚了哈,只有在isError()属性为真才会执行下边的代码,啥时候为真呢,就是调用了sendError()方法
依次进去看看
image

image

再次看到了熟悉的errorState属性。

在下边的代码中我们还可以看到将statusCodemessage放入了request中,名字是啥

image

public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
public static final String ERROR_MESSAGE = "javax.servlet.error.message";

所以在之后的异常处理时如果想获得sendError()时的参数,可以get这俩个属性得到

request.getAttribute("javax.servlet.error.status_code")
request.getAttribute("javax.servlet.error.message")

找到对应的异常处理配置之后,进入custom()方法

image

使用RequestDispatcher对象forward到配置的<location>继续对结果进行包装,这个<location>最终可以是个页面跳转,也可以是json字符串
如下

@RequestMapping("error.htm")
    public String error() {
        return "error.ftl";
    }
@RequestMapping(value = "noPermissionError.htm", produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    public Map<String, Object> noPermissionError(HttpServletRequest request) {

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", request.getAttribute("javax.servlet.error.status_code"));
        map.put("reason", request.getAttribute("javax.servlet.error.message"));
        return map;
    }

这边其实也可以改变状态码,可以使用@ResponseStatus注解,比如

 @RequestMapping(value = "noPermissionError.htm", produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public Map<String, Object> noPermissionError(HttpServletRequest request) {

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", request.getAttribute("javax.servlet.error.status_code"));
        map.put("reason", request.getAttribute("javax.servlet.error.message"));
        return map;
    }

这个状态码最后就是200,可以添加自己的业务错误码进行业务判断

附录

想在项目中跟踪到tomcat的类时需要在项目中添加依赖

<dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina</artifactId>
            <version>7.0.86</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-coyote</artifactId>
            <version>7.0.86</version>
            <scope>provided</scope>
        </dependency>

作用域一定要是provided,否则启动时会冲突,但是调试的时候发现还是有一些类不知道需要引入哪些包会缺失,大家可以到apache官网下载tomcat的源代码看,更方便

下载地址
http://tomcat.apache.org/download-60.cgi

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357