国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring Boot & Spring MVC 異常處理的N種方法

h9911 / 1744人閱讀

摘要:定制特定異常返回結(jié)果根據(jù)官方文檔的例子,可以使用和對(duì)特定異常返回特定的結(jié)果。下面是用瀏覽器和訪問(wèn)的結(jié)果無(wú)輸出注意上方表格的錯(cuò)誤,產(chǎn)生這個(gè)的原因前面已經(jīng)講過(guò)。不過(guò)需要注意的是,無(wú)法通過(guò)設(shè)定,由或者容器決定里一律是。

github:https://github.com/chanjarste...

參考文檔:

Spring Boot 1.5.4.RELEASE Documentation

Spring framework 4.3.9.RELEASE Documentation

Exception Handling in Spring MVC

默認(rèn)行為

根據(jù)Spring Boot官方文檔的說(shuō)法:

For machine clients it will produce a JSON response with details of the error, the HTTP status and the exception message. For browser clients there is a ‘whitelabel’ error view that renders the same data in HTML format

也就是說(shuō),當(dāng)發(fā)生異常時(shí):

如果請(qǐng)求是從瀏覽器發(fā)送出來(lái)的,那么返回一個(gè)Whitelabel Error Page

如果請(qǐng)求是從machine客戶端發(fā)送出來(lái)的,那么會(huì)返回相同信息的json

你可以在瀏覽器中依次訪問(wèn)以下地址:

http://localhost:8080/return-model-and-view

http://localhost:8080/return-view-name

http://localhost:8080/return-view

http://localhost:8080/return-text-plain

http://localhost:8080/return-json-1

http://localhost:8080/return-json-2

會(huì)發(fā)現(xiàn)FooController和FooRestController返回的結(jié)果都是一個(gè)Whitelabel Error Page也就是html。

但是如果你使用curl訪問(wèn)上述地址,那么返回的都是如下的json

{
  "timestamp": 1498886969426,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "me.chanjar.exception.SomeException",
  "message": "...",
  "trace": "...",
  "path": "..."
}

但是有一個(gè)URL除外:http://localhost:8080/return-text-plain,它不會(huì)返回任何結(jié)果,原因稍后會(huì)有說(shuō)明。

本章節(jié)代碼在me.chanjar.boot.def,使用DefaultExample運(yùn)行。

注意:我們必須在application.properties添加server.error.include-stacktrace=always才能夠得到stacktrace。

Spring MVC處理請(qǐng)求的總體流程

分析為何瀏覽器訪問(wèn)都Whitelabel Error Page

分析為何curl text/plain資源卻沒(méi)有返回結(jié)果

如果你在logback-spring.xml里一樣配置了這么一段:

那么你就能在日志文件里發(fā)現(xiàn)這么一個(gè)異常:

... TRACE 13387 --- [nio-8080-exec-2] .w.s.m.m.a.ServletInvocableHandlerMethod : Invoking "org.springframework.boot.autoconfigure.web.BasicErrorController.error" with arguments [org.apache.catalina.core.ApplicationHttpRequest@1408b81]
... TRACE 13387 --- [nio-8080-exec-2] .w.s.m.m.a.ServletInvocableHandlerMethod : Method [org.springframework.boot.autoconfigure.web.BasicErrorController.error] returned [<500 Internal Server Error,{timestamp=Thu Nov 09 13:20:15 CST 2017, status=500, error=Internal Server Error, exception=me.chanjar.exception.SomeException, message=No message available, trace=..., path=/return-text-plain, {}>]
... TRACE 13387 --- [nio-8080-exec-2] .w.s.m.m.a.ServletInvocableHandlerMethod : Error handling return value [type=org.springframework.http.ResponseEntity] [value=<500 Internal Server Error,{timestamp=Thu Nov 09 13:20:15 CST 2017, status=500, error=Internal Server Error, exception=me.chanjar.exception.SomeException, message=No message available, trace=..., path=/return-text-plain, {}>]
HandlerMethod details: 
Controller [org.springframework.boot.autoconfigure.web.BasicErrorController]
Method [public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
...

要理解這個(gè)異常是怎么來(lái)的,那我們來(lái)簡(jiǎn)單分析以下Spring MVC的處理過(guò)程:

那么這個(gè)問(wèn)題怎么解決呢?我會(huì)在自定義ErrorController里說(shuō)明。

自定義Error頁(yè)面

前面看到了,Spring Boot針對(duì)瀏覽器發(fā)起的請(qǐng)求的error頁(yè)面是Whitelabel Error Page,下面講解如何自定義error頁(yè)面。

注意2:自定義Error頁(yè)面不會(huì)影響machine客戶端的輸出結(jié)果

方法1

根據(jù)Spring Boot官方文檔,如果想要定制這個(gè)頁(yè)面只需要:

to customize it just add a View that resolves to ‘error’

這句話講的不是很明白,其實(shí)只要看ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration的代碼就知道,只需注冊(cè)一個(gè)名字叫做errorView類型的Bean就行了。

本例的CustomDefaultErrorViewConfiguration注冊(cè)將error頁(yè)面改到了templates/custom-error-page/error.html上。

本章節(jié)代碼在me.chanjar.boot.customdefaulterrorview,使用CustomDefaultErrorViewExample運(yùn)行。

方法2

方法2比方法1簡(jiǎn)單很多,在Spring官方文檔中沒(méi)有說(shuō)明。其實(shí)只需要提供error View所對(duì)應(yīng)的頁(yè)面文件即可。

比如在本例里,因?yàn)槭褂玫氖荰hymeleaf模板引擎,所以在classpath /templates放一個(gè)自定義的error.html就能夠自定義error頁(yè)面了。

本章節(jié)就不提供代碼了,有興趣的你可以自己嘗試。

自定義Error屬性

前面看到了不論error頁(yè)面還是error json,能夠得到的屬性就只有:timestamp、status、error、exception、message、trace、path。

如果你想自定義這些屬性,可以如Spring Boot官方文檔所說(shuō)的:

simply add a bean of type ErrorAttributes to use the existing mechanism but replace the contents

ErrorMvcAutoConfiguration.errorAttributes提供了DefaultErrorAttributes,我們也可以參照這個(gè)提供一個(gè)自己的CustomErrorAttributes覆蓋掉它。

如果使用curl訪問(wèn)相關(guān)地址可以看到,返回的json里的出了修改過(guò)的屬性,還有添加的屬性:

{
  "exception": "customized exception",
  "add-attribute": "add-attribute",
  "path": "customized path",
  "trace": "customized trace",
  "error": "customized error",
  "message": "customized message",
  "timestamp": 1498892609326,
  "status": 100
}

本章節(jié)代碼在me.chanjar.boot.customerrorattributes,使用CustomErrorAttributesExample運(yùn)行。

自定義ErrorController

在前面提到了curl http://localhost:8080/return-text-plain得不到error信息,解決這個(gè)問(wèn)題有兩個(gè)關(guān)鍵點(diǎn):

請(qǐng)求的時(shí)候指定Accept頭,避免匹配到BasicErrorController.error方法。比如:curl -H "Accept: text/plain" http://localhost:8080/return-text-plain

提供自定義的ErrorController提供一個(gè)path=/error procudes=text/plain的方法。

其實(shí)還有另一種方式:提供一個(gè)Object->String轉(zhuǎn)換的HttpMessageConverter,這個(gè)方法本文不展開。

下面將如何提供自定義的ErrorController。按照Spring Boot官方文檔的說(shuō)法:

To do that just extend BasicErrorController and add a public method with a @RequestMapping that has a produces attribute, and create a bean of your new type.

所以我們提供了一個(gè)CustomErrorController,并且通過(guò)CustomErrorControllerConfiguration將其注冊(cè)為Bean。

本章節(jié)代碼在me.chanjar.boot.customerrorcontroller,使用CustomErrorControllerExample運(yùn)行。

ControllerAdvice定制特定異常返回結(jié)果

根據(jù)Spring Boot官方文檔的例子,可以使用@ControllerAdvice和@ExceptionHandler對(duì)特定異常返回特定的結(jié)果。

我們?cè)谶@里定義了一個(gè)新的異常:AnotherException,然后在BarControllerAdvice中對(duì)SomeException和AnotherException定義了不同的@ExceptionHandler:

SomeException都返回到controlleradvice/some-ex-error.html

AnotherException統(tǒng)統(tǒng)返回ResponseEntity

在BarController中,所有*-a都拋出SomeException,所有*-b都拋出AnotherException。下面是用瀏覽器和curl訪問(wèn)的結(jié)果:

url Browser curl
http://localhost:8080/bar/html-a some-ex-error.html some-ex-error.html
http://localhost:8080/bar/html-b error(json) error(json)
http://localhost:8080/bar/json-a some-ex-error.html some-ex-error.html
http://localhost:8080/bar/json-b error(json) error(json)
http://localhost:8080/bar/text-plain-a some-ex-error.html some-ex-error.html
http://localhost:8080/bar/text-plain-b Could not find acceptable representation(White Error Page) Could not find acceptable representation(無(wú)輸出)

注意上方表格的Could not find acceptable representation錯(cuò)誤,產(chǎn)生這個(gè)的原因前面已經(jīng)講過(guò)。

不過(guò)需要注意的是流程稍微有點(diǎn)不同,在前面的例子里的流程是這樣的:

訪問(wèn)url

拋出異常

forward到 /error

BasicErrorController.error方法返回的ResponseEntity沒(méi)有辦法轉(zhuǎn)換成String

本章節(jié)例子的異常是這樣的:

訪問(wèn)url

拋出異常

@ExceptionHandler處理

AnotherException的@ExceptionHander返回的ResponseEntity沒(méi)有辦法轉(zhuǎn)換成String,被算作沒(méi)有被處理成功

forward到 /error

BasicErrorController.error方法返回的ResponseEntity沒(méi)有辦法轉(zhuǎn)換成String

所以你會(huì)發(fā)現(xiàn)如果使用@ExceptionHandler,那就得自己根據(jù)請(qǐng)求頭Accept的不同而輸出不同的結(jié)果了,辦法就是定義一個(gè)void @ExceptionHandler,具體見@ExceptionHandler javadoc。

定制不同Status Code的錯(cuò)誤頁(yè)面

Spring Boot 官方文檔提供了一種簡(jiǎn)單的根據(jù)不同Status Code跳到不同error頁(yè)面的方法,見這里。

我們可以將不同的Status Code的頁(yè)面放在classpath: public/errorclasspath: templates/error目錄下,比如400.html5xx.html400.ftl5xx.ftl

打開瀏覽器訪問(wèn)以下url會(huì)獲得不同的結(jié)果:

url Result
http://localhost:8080/loo/error-403 static resource: public/error/403.html
http://localhost:8080/loo/error-406 thymeleaf view: templates/error/406.html
http://localhost:8080/loo/error-600 Whitelabel error page
http://localhost:8080/loo/error-601 thymeleaf view: templates/error/6xx.html

注意/loo/error-600返回的是Whitelabel error page,但是/loo/error-403loo/error-406能夠返回我們期望的錯(cuò)誤頁(yè)面,這是為什么?先來(lái)看看代碼。

loo/error-403中,我們拋出了異常Exception403

@ResponseStatus(HttpStatus.FORBIDDEN)
public class Exception403 extends RuntimeException

loo/error-406中,我們拋出了異常Exception406

@ResponseStatus(NOT_ACCEPTABLE)
public class Exception406 extends RuntimeException

注意到這兩個(gè)異常都有@ResponseStatus注解,這個(gè)是注解標(biāo)明了這個(gè)異常所對(duì)應(yīng)的Status Code。
但是在loo/error-600中拋出的SomeException沒(méi)有這個(gè)注解,而是嘗試在Response.setStatus(600)來(lái)達(dá)到目的,但結(jié)果是失敗的,這是為什么呢?:

@RequestMapping("/error-600")
public String error600(HttpServletRequest request, HttpServletResponse response) throws SomeException {
  request.setAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE, 600);
  response.setStatus(600);
  throw new SomeException();
}

要了解為什么就需要知道Spring MVC對(duì)于異常的處理機(jī)制,下面簡(jiǎn)單講解一下:

Spring MVC處理異常的地方在DispatcherServlet.processHandlerException,這個(gè)方法會(huì)利用HandlerExceptionResolver來(lái)看異常應(yīng)該返回什么ModelAndView

目前已知的HandlerExceptionResolver有這么幾個(gè):

DefaultErrorAttributes,只負(fù)責(zé)把異常記錄在Request attributes中,name是org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.ERROR

ExceptionHandlerExceptionResolver,根據(jù)@ExceptionHandler resolve

ResponseStatusExceptionResolver,根據(jù)@ResponseStatus resolve

DefaultHandlerExceptionResolver,負(fù)責(zé)處理Spring MVC標(biāo)準(zhǔn)異常

Exception403Exception406都有被ResponseStatusExceptionResolver處理了,而SomeException沒(méi)有任何Handler處理,這樣DispatcherServlet就會(huì)將這個(gè)異常往上拋至到容器處理(見DispatcherServlet#L1243),以Tomcat為例,它在StandardHostValve#L317、StandardHostValve#L345會(huì)將Status Code設(shè)置成500,然后forward到/error,結(jié)果就是BasicErrorController處理時(shí)就看到Status Code=500,然后按照500去找error page找不到,就只能返回White error page了。

實(shí)際上,從Request的attributes角度來(lái)看,交給BasicErrorController處理時(shí),和容器自己處理時(shí),有幾個(gè)相關(guān)屬性的內(nèi)部情況時(shí)這樣的:

Attribute name When throw up to Tomcat Handled by HandlerExceptionResolver
DefaultErrorAttributes.ERROR Has value Has Value
DispatcherServlet.EXCEPTION No value Has Value
javax.servlet.error.exception Has value No Value

PS. DefaultErrorAttributes.ERROR = org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.ERROR

PS. DispatcherServlet.EXCEPTION = org.springframework.web.servlet.DispatcherServlet.EXCEPTION

解決辦法有兩個(gè):

SomeException添加@ResponseStatus,但是這個(gè)方法有兩個(gè)局限:

如果這個(gè)異常不是你能修改的,比如在第三方的Jar包里

如果@ResponseStatus使用HttpStatus作為參數(shù),但是這個(gè)枚舉定義的Status Code數(shù)量有限

使用@ExceptionHandler,不過(guò)得注意自己決定view以及status code

第二種解決辦法的例子loo/error-601,對(duì)應(yīng)的代碼:

@RequestMapping("/error-601")
public String error601(HttpServletRequest request, HttpServletResponse response) throws AnotherException {
  throw new AnotherException();
}

@ExceptionHandler(AnotherException.class)
String handleAnotherException(HttpServletRequest request, HttpServletResponse response, Model model)
    throws IOException {
  // 需要設(shè)置Status Code,否則響應(yīng)結(jié)果會(huì)是200
  response.setStatus(601);
  model.addAllAttributes(errorAttributes.getErrorAttributes(new ServletRequestAttributes(request), true));
  return "error/6xx";
}

總結(jié):

沒(méi)有被HandlerExceptionResolverresolve到的異常會(huì)交給容器處理。已知的實(shí)現(xiàn)有(按照順序):

DefaultErrorAttributes,只負(fù)責(zé)把異常記錄在Request attributes中,name是org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.ERROR

ExceptionHandlerExceptionResolver,根據(jù)@ExceptionHandler resolve

ResponseStatusExceptionResolver,根據(jù)@ResponseStatus resolve

DefaultHandlerExceptionResolver,負(fù)責(zé)處理Spring MVC標(biāo)準(zhǔn)異常

@ResponseStatus用來(lái)規(guī)定異常對(duì)應(yīng)的Status Code,其他異常的Status Code由容器決定,在Tomcat里都認(rèn)定為500(StandardHostValve#L317、StandardHostValve#L345)

@ExceptionHandler處理的異常不會(huì)經(jīng)過(guò)BasicErrorController,需要自己決定如何返回頁(yè)面,并且設(shè)置Status Code(如果不設(shè)置就是200)

BasicErrorController會(huì)嘗試根據(jù)Status Code找error page,找不到的話就用Whitelabel error page

本章節(jié)代碼在me.chanjar.boot.customstatuserrorpage,使用CustomStatusErrorPageExample運(yùn)行。

利用ErrorViewResolver來(lái)定制錯(cuò)誤頁(yè)面

前面講到BasicErrorController會(huì)根據(jù)Status Code來(lái)跳轉(zhuǎn)對(duì)應(yīng)的error頁(yè)面,其實(shí)這個(gè)工作是由DefaultErrorViewResolver完成的。

實(shí)際上我們也可以提供自己的ErrorViewResolver來(lái)定制特定異常的error頁(yè)面。

@Component
public class SomeExceptionErrorViewResolver implements ErrorViewResolver {

  @Override
  public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) {
    return new ModelAndView("custom-error-view-resolver/some-ex-error", model);
  }

}

不過(guò)需要注意的是,無(wú)法通過(guò)ErrorViewResolver設(shè)定Status Code,Status Code由@ResponseStatus或者容器決定(Tomcat里一律是500)。

本章節(jié)代碼在me.chanjar.boot.customerrorviewresolver,使用CustomErrorViewResolverExample運(yùn)行。

@ExceptionHandler 和 @ControllerAdvice

前面的例子中已經(jīng)有了對(duì)@ControllerAdvice和@ExceptionHandler的使用,這里只是在做一些補(bǔ)充說(shuō)明:

@ExceptionHandler配合@ControllerAdvice用時(shí),能夠應(yīng)用到所有被@ControllerAdvice切到的Controller

@ExceptionHandler在Controller里的時(shí)候,就只會(huì)對(duì)那個(gè)Controller生效

最佳實(shí)踐

前面講了那么多種方式,那么在Spring MVC中處理異常的最佳實(shí)踐是什么?在回答這個(gè)問(wèn)題前我先給出一個(gè)好的異常處理應(yīng)該是什么樣子的:

返回的異常信息能夠適配各種Accept,比如Accept:text/html返回html頁(yè)面,Accept:application/json返回json。

統(tǒng)一的異常信息schema,且可自定義,比如只包含timestamperrormessage等信息。

能夠自定義部分信息,比如可以自定義errormessage的內(nèi)容。

要達(dá)成以上目標(biāo)我們可以采取的方法:

達(dá)成第1條:自定義ErrorController,擴(kuò)展BasicErrorController,支持更多的Accept類型。

達(dá)成第2條:自定義ErrorAttributes

達(dá)成第3條:

使用@ResponseStatusResponseStatusException(since 5.0)

前一種方式不適用時(shí),自定義ErrorAttributes,在里面寫代碼,針對(duì)特定異常返回特定信息。推薦使用配置的方式來(lái)做,比如配置文件里寫XXXException的message是YYYY。

Spring MVC對(duì)于從Controller拋出的異常是不打印到console的,解決辦法是提供一個(gè)HandlerExceptionResolver,比如這樣:

@Order(Ordered.HIGHEST_PRECEDENCE)
public class ErrorLogger implements HandlerExceptionResolver {

  private static final Logger LOGGER = LoggerFactory.getLogger(ErrorLogger.class);

  @Override
  public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
      Exception ex) {
    LOGGER.error("Exception happened at [{}]: {}", request.getRequestURI(), ExceptionUtils.getStackTrace(ex));
    return null;
  }

}
附錄I

下表列出哪些特性是Spring Boot的,哪些是Spring MVC的:

Feature Spring Boot Spring MVC
BasicErrorController Yes
ErrorAttributes Yes
ErrorViewResolver Yes
@ControllerAdvice Yes
@ExceptionHandler Yes
@ResponseStatus Yes
HandlerExceptionResolver Yes

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/67301.html

相關(guān)文章

  • Spring體系常用項(xiàng)目一覽

    摘要:的面向的異常遵從通用的異常層次結(jié)構(gòu)。比如以前常用的框架,現(xiàn)在常用的框架包含許多項(xiàng)目,下面挑一些最常用的出來(lái)總結(jié)一下。狀態(tài)是流程中事件發(fā)生的地點(diǎn),在流程中通過(guò)轉(zhuǎn)移的方式從一個(gè)狀態(tài)到另一個(gè)狀態(tài),流程的當(dāng)前狀況稱為流程數(shù)據(jù)。 如今做Java尤其是web幾乎是避免不了和Spring打交道了,但是Spring是這樣的大而全,新鮮名詞不斷產(chǎn)生,學(xué)起來(lái)給人一種凌亂的感覺(jué),我就在這里總結(jié)一下,理順頭緒...

    OnlyLing 評(píng)論0 收藏0
  • Java 最常見 200+ 面試題全解析:面試必備(附答案)

    摘要:的簡(jiǎn)稱,運(yùn)行環(huán)境,為的運(yùn)行提供了所需環(huán)境。分割字符串,返回一個(gè)分割后的字符串?dāng)?shù)組。線程安全是線程安全的,而是非線程安全的。迭代器取代了集合框架中的,迭代器允許調(diào)用者在迭代過(guò)程中移除元素。 本文分為十九個(gè)模塊,分別是:?Java 基礎(chǔ)、容器、多線程、反射、對(duì)象拷貝、Java Web 、異常、網(wǎng)絡(luò)、設(shè)計(jì)模式、Spring/Spring MVC、Spring Boot/Spring Clou...

    hufeng 評(píng)論0 收藏0
  • Spring Boot 2.x 系列教程:WebFlux REST API 全局異常處理 Error

    摘要:挺多人咨詢的,異常處理用切面注解去實(shí)現(xiàn)去全局異常處理。全局異常處理類,代碼如下代碼解析如下抽象類是用來(lái)處理全局錯(cuò)誤時(shí)進(jìn)行擴(kuò)展和實(shí)現(xiàn)注解標(biāo)記的切面排序,值越小擁有越高的優(yōu)先級(jí),這里設(shè)置優(yōu)先級(jí)偏高。 本文內(nèi)容 為什么要全局異常處理? WebFlux REST 全局異常處理實(shí)戰(zhàn) 小結(jié) 摘錄:只有不斷培養(yǎng)好習(xí)慣,同時(shí)不斷打破壞習(xí)慣,我們的行為舉止才能夠自始至終都是正確的。 一、為什么要全局...

    BicycleWarrior 評(píng)論0 收藏0
  • Spring-Boot學(xué)習(xí)筆記

    摘要:學(xué)習(xí)筆記使用很容易創(chuàng)建一個(gè)獨(dú)立運(yùn)行運(yùn)行內(nèi)嵌容器準(zhǔn)生產(chǎn)級(jí)別的基于框架的項(xiàng)目,使用你可以不用或者只需要很少的配置。異常消息如果這個(gè)錯(cuò)誤是由異常引起的。錯(cuò)誤發(fā)生時(shí)請(qǐng)求的路徑。 Spring-Boot 1.5 學(xué)習(xí)筆記 使用Spring Boot很容易創(chuàng)建一個(gè)獨(dú)立運(yùn)行(運(yùn)行jar,內(nèi)嵌Servlet容器)、準(zhǔn)生產(chǎn)級(jí)別的基于Spring框架的項(xiàng)目,使用Spring Boot你可以不用或者只需要很...

    curlyCheng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<