SpringBoot--Web篇<三>

SpringBoot--Web篇<一>
SpringBoot--Web篇<二>


7. 錯(cuò)誤處理機(jī)制

7.1 SpringBoot默認(rèn)的錯(cuò)誤處理機(jī)制

默認(rèn)效果:

1)瀏覽器,返回一個(gè)默認(rèn)的錯(cuò)誤頁(yè)面

404

此時(shí)可以查看瀏覽器發(fā)送請(qǐng)求的請(qǐng)求頭:

image

如果是其他客戶端,默認(rèn)響應(yīng)一個(gè)json數(shù)據(jù)

{
    "timestamp": 1534750087344,
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/aaa"
}

原理:

? 可以參照ErrorMvcAutoConfiguration : 錯(cuò)誤處理的自動(dòng)配置類;

? 說(shuō)明:基于SpringBoot 1.5.x 源碼;

? 給容器中添加了以下組件:

? 1手形、DefaultErrorAttributes:幫我們?cè)陧?yè)面共享信息;

//ErrorMvcAutoConfiguration.java
    @Bean
    @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
    public DefaultErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes();
    }

// DefaultErrorAttributes.java
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
            boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
        errorAttributes.put("timestamp", new Date());
        addStatus(errorAttributes, requestAttributes);
        addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
        addPath(errorAttributes, requestAttributes);
        return errorAttributes;
    }

? 2、BasicErrorController: 處理默認(rèn)的 /error請(qǐng)求

//ErrorMvcAutoConfiguration.java
    @Bean
    @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
    public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
        return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
                this.errorViewResolvers);
    }

//BasicErrorController.java 
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
  
    //產(chǎn)生html類型的數(shù)據(jù);瀏覽器發(fā)送的請(qǐng)求來(lái)到這個(gè)方法處理
    @RequestMapping(produces = "text/html")
    public ModelAndView errorHtml(HttpServletRequest request,
            HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        //去哪個(gè)頁(yè)面作為錯(cuò)誤頁(yè)面翰守;包含頁(yè)面地址和頁(yè)面內(nèi)容
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
    }

    ////產(chǎn)生json數(shù)據(jù),其他客戶端來(lái)到這個(gè)方法處理疲酌;
    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }

? 3蜡峰、ErrorPageCustomizer

@Value("${error.path:/error}")
    private String path = "/error";  //系統(tǒng)出現(xiàn)錯(cuò)誤以后來(lái)到error請(qǐng)求進(jìn)行處理了袁;(web.xml注冊(cè)的錯(cuò)誤頁(yè)面規(guī)則)

? 4.、DefaultErrorViewResolver

//ErrorMvcAutoConfiguration.java        
        @Bean
        @ConditionalOnBean(DispatcherServlet.class)
        @ConditionalOnMissingBean
        public DefaultErrorViewResolver conventionErrorViewResolver() {
            return new DefaultErrorViewResolver(this.applicationContext,
                    this.resourceProperties);
        }


//DefaultErrorViewResolver.java
    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
            Map<String, Object> model) {
        ModelAndView modelAndView = resolve(String.valueOf(status), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        String errorViewName = "error/" + viewName;
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
                .getProvider(errorViewName, this.applicationContext);
        if (provider != null) {
            return new ModelAndView(errorViewName, model);
        }
        return resolveResource(errorViewName, model);
    }   

? 步驟:

? 一但系統(tǒng)出現(xiàn)4xx或者5xx之類的錯(cuò)誤湿颅;ErrorPageCustomizer就會(huì)生效(定制錯(cuò)誤的響應(yīng)規(guī)則)载绿;就會(huì)來(lái)到/error請(qǐng)求;就會(huì)被BasicErrorController處理油航;

? 響應(yīng)頁(yè)面崭庸;去哪個(gè)頁(yè)面是由DefaultErrorViewResolver解析得到的;

protected ModelAndView resolveErrorView(HttpServletRequest request,
      HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    //所有的ErrorViewResolver得到ModelAndView
   for (ErrorViewResolver resolver : this.errorViewResolvers) {
      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
      if (modelAndView != null) {
         return modelAndView;
      }
   }
   return null;
}

7.2 如何定制錯(cuò)誤響應(yīng)

7.2.1 如何定制錯(cuò)誤的頁(yè)面

? 1) 有模板引擎的情況下谊囚;error/狀態(tài)碼; 【將錯(cuò)誤頁(yè)面命名為 錯(cuò)誤狀態(tài)碼.html 放在模板引擎文件夾里面的 error文件夾下】怕享,發(fā)生此狀態(tài)碼的錯(cuò)誤就會(huì)來(lái)到 對(duì)應(yīng)的頁(yè)面;

? 我們可以使用4xx和5xx作為錯(cuò)誤頁(yè)面的文件名來(lái)匹配這種類型的所有錯(cuò)誤镰踏,精確優(yōu)先(優(yōu)先尋找精確的狀態(tài)碼.html)函筋;

頁(yè)面能獲取的信息:

                timestamp:時(shí)間戳

                status:狀態(tài)碼

                error:錯(cuò)誤提示

                exception:異常對(duì)象

                message:異常消息

                errors:JSR303數(shù)據(jù)校驗(yàn)的錯(cuò)誤都在這里

? 2) 沒(méi)有模板引擎(模板引擎找不到這個(gè)錯(cuò)誤頁(yè)面),靜態(tài)資源文件夾下找奠伪;

? 3)以上都沒(méi)有錯(cuò)誤頁(yè)面跌帐,就是默認(rèn)來(lái)到SpringBoot默認(rèn)的錯(cuò)誤提示頁(yè)面;

7.2.2 如何定制錯(cuò)誤的json數(shù)據(jù)

? 1)自定義異常處理&返回定制json數(shù)據(jù)芳来;

//MyExceptionHandler.java
@ControllerAdvice
public class MyExceptionHandler {

    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        return map;
    }
}

? 2)轉(zhuǎn)發(fā)到/error進(jìn)行自適應(yīng)響應(yīng)效果處理

    @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request){
         //傳入我們自己的錯(cuò)誤狀態(tài)碼  4xx 5xx含末,否則就不會(huì)進(jìn)入定制錯(cuò)誤頁(yè)面的解析流程
        /**
         * Integer statusCode = (Integer) request
         .getAttribute("javax.servlet.error.status_code");
         */
        request.setAttribute("javax.servlet.error.status_code",500);
        Map<String,Object> map = new HashMap<>();
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        //轉(zhuǎn)發(fā)到/error
        return "forward:/error";
    }

? 3)將我們的定制數(shù)據(jù)攜帶出去;

? 出現(xiàn)錯(cuò)誤以后即舌,會(huì)來(lái)到/error請(qǐng)求佣盒,會(huì)被BasicErrorController處理,響應(yīng)出去可以獲取的數(shù)據(jù)是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)規(guī)定的方法)顽聂;

? 1肥惭、完全來(lái)編寫一個(gè)ErrorController的實(shí)現(xiàn)類【或者是編寫AbstractErrorController的子類】,放在容器中紊搪;

? 2蜜葱、頁(yè)面上能用的數(shù)據(jù),或者是json返回能用的數(shù)據(jù)都是通過(guò)errorAttributes.getErrorAttributes得到耀石;

? 容器中DefaultErrorAttributes.getErrorAttributes()牵囤;默認(rèn)進(jìn)行數(shù)據(jù)處理的;

自定義ErrorAttributes

package com.example.springboot_restcrud.component;

import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;

import java.util.Map;

/**
 * 向容器中注入我們自定義的 ErrorAttributes
 * @Author cyy
 * @Date 2018/8/20 17:10
 * @Version 1.0
 * @Blog http://pccwcyy.club/wordpress/
 **/
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
        map.put("company","pccw");

        //我們的異常處理器攜帶的數(shù)據(jù)
//        Map<String, Object> ext = (Map<String, Object>) requestAttributes.getAttribute("ext",0);
//        map.put("ext",ext);
        return map;
    }
}

最終的效果:響應(yīng)是自適應(yīng)的滞伟,可以通過(guò)定制ErrorAttributes改變需要返回的內(nèi)容

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末揭鳞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梆奈,更是在濱河造成了極大的恐慌野崇,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亩钟,死亡現(xiàn)場(chǎng)離奇詭異乓梨,居然都是意外死亡鳖轰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門扶镀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕴侣,“玉大人,你說(shuō)我怎么就攤上這事狈惫【χ耄” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵胧谈,是天一觀的道長(zhǎng)忆肾。 經(jīng)常有香客問(wèn)我,道長(zhǎng)菱肖,這世上最難降的妖魔是什么客冈? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮稳强,結(jié)果婚禮上场仲,老公的妹妹穿的比我還像新娘。我一直安慰自己退疫,他們只是感情好渠缕,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著褒繁,像睡著了一般亦鳞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棒坏,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天燕差,我揣著相機(jī)與錄音,去河邊找鬼坝冕。 笑死徒探,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喂窟。 我是一名探鬼主播测暗,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼磨澡!你這毒婦竟也來(lái)了碗啄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钱贯,失蹤者是張志新(化名)和其女友劉穎挫掏,沒(méi)想到半個(gè)月后侦另,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秩命,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尉共,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弃锐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袄友。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霹菊,靈堂內(nèi)的尸體忽然破棺而出剧蚣,到底是詐尸還是另有隱情,我是刑警寧澤旋廷,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布鸠按,位于F島的核電站,受9級(jí)特大地震影響饶碘,放射性物質(zhì)發(fā)生泄漏目尖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一扎运、第九天 我趴在偏房一處隱蔽的房頂上張望瑟曲。 院中可真熱鬧,春花似錦豪治、人聲如沸洞拨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)烦衣。三九已至,卻和暖如春齿椅,著一層夾襖步出監(jiān)牢的瞬間琉挖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工涣脚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留示辈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓遣蚀,卻偏偏與公主長(zhǎng)得像矾麻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芭梯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理险耀,服務(wù)發(fā)現(xiàn),斷路器玖喘,智...
    卡卡羅2017閱讀 134,704評(píng)論 18 139
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架甩牺,建立于...
    Hsinwong閱讀 22,435評(píng)論 1 92
  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明先生_X自主閱讀 15,988評(píng)論 3 119
  • 1.只要想著解決問(wèn)題累奈,不管多難的問(wèn)題贬派,看書就不會(huì)枯燥和走神急但。 2、就像肖生克里面的瑞恩一樣搞乏,需要和別人交朋友波桩,需要...
    智囊團(tuán)閱讀 121評(píng)論 0 0
  • 現(xiàn)在回頭想,我其實(shí)一直是一個(gè)缺乏自信的人请敦。從小到大镐躲,長(zhǎng)久以來(lái)不自信的種子一直深深地埋在我的身體里。 我...
    瑞恩126閱讀 163評(píng)論 0 0