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

分類:IT技術 時間:2017-07-05
github:https://github.com/chanjarster/spring-mvc-error-handling-example

參考文檔:

* Spring Boot 1.5.4.RELEASE [Documentation][spring-boot-doc]
* Spring framework 4.3.9.RELEASE [Documentation][spring-mvc-doc]
* [Exception Handling in Spring MVC][blog-exception-handling-in-spring-mvc]


## 默認行為

根據Spring Boot官方文檔的說法:

> 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

也就是說,當發生異常時:

* 如果請求是從瀏覽器發送出來的,那麽返回一個`Whitelabel Error Page`
* 如果請求是從machine客戶端發送出來的,那麽會返回相同信息的`json`

你可以在瀏覽器中依次訪問以下地址:

1. `http://localhost:8080/return-model-and-view`
1. `http://localhost:8080/return-view-name`
1. `http://localhost:8080/return-view`
1. `http://localhost:8080/return-text-plain`
1. `http://localhost:8080/return-json-1`
1. `http://localhost:8080/return-json-2`

會發現[FooController][def-foo]和[FooRestController][def-foo-rest]返回的結果都是一個`Whitelabel Error Page`也就是html。

但是如果你使用`curl`訪問上述地址,那麽返回的都是如下的`json`:

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

但是有一個URL除外:`http://localhost:8080/return-text-plain`,它不會返回任何結果,原因稍後會有說明。

本章節代碼在[me.chanjar.boot.def][pkg-me.chanjar.boot.def],使用[DefaultExample][boot-DefaultExample]運行。

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

### 為何curl text/plain資源無法獲得error

如果你在[logback-spring.xml][logback-spring.xml]裏一樣配置了這麽一段:

```xml
<logger name="org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod" level="TRACE"/>
```

那麽你就能在日誌文件裏發現這麽一個異常:

```
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
...
```

要理解這個異常是怎麽來的,那我們來簡單分析以下Spring MVC的處理過程:

1. `curl http://localhost:8080/return-text-plain`,會隱含一個請求頭`Accept: */*`,會匹配到``FooController.returnTextPlain(produces=text/plain)``方法,註意:如果請求頭不是``Accept: */*``或``Accept: text/plain``,那麽是匹配不到``FooController.returnTextPlain``的。
1. [RequestMappingHandlerMapping][RequestMappingHandlerMapping]根據url匹配到了(見[AbstractHandlerMethodMapping.lookupHandlerMethod#L341][AbstractHandlerMethodMapping_L341])``FooController.returnTextPlan``(``produces=text/plain``)。
1. 方法拋出了異常,forward到`/error`。
1. [RequestMappingHandlerMapping][RequestMappingHandlerMapping]根據url匹配到了(見[AbstractHandlerMethodMapping.lookupHandlerMethod#L341][AbstractHandlerMethodMapping_L341])[BasicErrorController][BasicErrorController]的兩個方法[errorHtml][BasicErrorController_errorHtml](``produces=text/html``)和[error][BasicErrorController_error](``produces=null``,相當於``produces=*/*``)。
1. 因為請求頭`Accept: */*`,所以會匹配[error][BasicErrorController_error]方法上(見[AbstractHandlerMethodMapping#L352][AbstractHandlerMethodMapping_L352],[RequestMappingInfo.compareTo][RequestMappingInfo_L266],[ProducesRequestCondition.compareTo][ProducesRequestCondition_L235])。
1. `error`方法返回的是``ResponseEntity<Map<String, Object>>``,會被[HttpEntityMethodProcessor.handleReturnValue][HttpEntityMethodProcessor_L159]處理。
1. [HttpEntityMethodProcessor][HttpEntityMethodProcessor]進入[AbstractMessageConverterMethodProcessor.writeWithMessageConverters][AbstractMessageConverterMethodProcessor_L163],發現請求要求``*/*``(``Accept: */*``),而能夠產生``text/plain``(``FooController.returnTextPlan produces=text/plain``),那它會去找能夠將`Map`轉換成`String`的[HttpMessageConverter][HttpMessageConverter](``text/plain``代表``String``),結果是找不到。
1. [AbstractMessageConverterMethodProcessor][AbstractMessageConverterMethodProcessor]拋出[HttpMediaTypeNotAcceptableException][AbstractMessageConverterMethodProcessor_L259]。


那麽為什麽瀏覽器訪問`http://localhost:8080/return-text-plain`就可以呢?你只需打開瀏覽器的開發者模式看看請求頭就會發現`Accept:text/html,...`,所以在第4步會匹配到[BasicErrorController.errorHtml][BasicErrorController_errorHtml]方法,那結果自然是沒有問題了。

那麽這個問題怎麽解決呢?我會在*自定義ErrorController*裏說明。

## 自定義Error頁面

前面看到了,Spring Boot針對瀏覽器發起的請求的error頁面是`Whitelabel Error Page`,下面講解如何自定義error頁面。

註意2:自定義Error頁面不會影響machine客戶端的輸出結果

### 方法1

根據Spring Boot官方文檔,如果想要定制這個頁面只需要:

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

這句話講的不是很明白,其實只要看`ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration`的代碼就知道,只需註冊一個名字叫做`error`的`View`類型的`Bean`就行了。

本例的[CustomDefaultErrorViewConfiguration][boot-CustomDefaultErrorViewConfiguration]註冊將`error`頁面改到了[templates/custom-error-page/error.html][boot-custom-error-page-error-html]上。

本章節代碼在[me.chanjar.boot.customdefaulterrorview][pkg-me.chanjar.boot.customdefaulterrorview],使用[CustomDefaultErrorViewExample][boot-CustomDefaultErrorViewExample]運行。

### 方法2

方法2比方法1簡單很多,在Spring官方文檔中沒有說明。其實只需要提供`error` `View`所對應的頁面文件即可。

比如在本例裏,因為使用的是Thymeleaf模板引擎,所以在classpath `/templates`放一個自定義的`error.html`就能夠自定義error頁面了。

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

## 自定義Error屬性

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

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

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

在`ErrorMvcAutoConfiguration.errorAttributes`提供了[DefaultErrorAttributes][spring-DefaultErrorAttributes-Javadoc],我們也可以參照這個提供一個自己的[CustomErrorAttributes][boot-CustomErrorAttributes]覆蓋掉它。

如果使用curl訪問相關地址可以看到,返回的json裏的出了修改過的屬性,還有添加的屬性:

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

本章節代碼在[me.chanjar.boot.customerrorattributes][pkg-me.chanjar.boot.customerrorattributes],使用[CustomErrorAttributesExample][boot-CustomErrorAttributesExample]運行。

## 自定義ErrorController

在前面提到了`curl http://localhost:8080/return-text-plain`得不到error信息,解決這個問題有兩個關鍵點:

1. 請求的時候指定`Accept`頭,避免匹配到[BasicErrorController.error][BasicErrorController_error]方法。比如:`curl -H 'Accept: text/plain' http://localhost:8080/return-text-plain`
1. 提供自定義的``ErrorController``。

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

> 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.

所以我們提供了一個[CustomErrorController][boot-CustomErrorController],並且通過[CustomErrorControllerConfiguration][boot-CustomErrorControllerConfiguration]將其註冊為Bean。

本章節代碼在[me.chanjar.boot.customerrorcontroller][pkg-me.chanjar.boot.customerrorcontroller],使用[CustomErrorControllerExample][boot-CustomErrorControllerExample]運行。

## ControllerAdvice定制特定異常返回結果

根據Spring Boot官方文檔的例子,可以使用[@ControllerAdvice][spring-ControllerAdvice]和[@ExceptionHandler][spring-ExceptionHandler]對特定異常返回特定的結果。

我們在這裏定義了一個新的異常:AnotherException,然後在[BarControllerAdvice][boot-BarControllerAdvice]中對SomeException和AnotherException定義了不同的[@ExceptionHandler][spring-ExceptionHandler]:

* SomeException都返回到`controlleradvice/some-ex-error.html`上
* AnotherException統統返回JSON

在[BarController][boot-BarController]中,所有`*-a`都拋出``SomeException``,所有`*-b`都拋出``AnotherException``。下面是用瀏覽器和curl訪問的結果:

| url                                    | Browser                                                                                 | curl               |
| -------------------------------------- |-----------------------------------------------------------------------------------------| -------------------|
| http://localhost:8080/bar/html-a       | some-ex-error.html                                                                      | some-ex-error.html |
| http://localhost:8080/bar/html-b       | No converter found for return value of type: class AnotherExceptionErrorMessage[AbstractMessageConverterMethodProcessor#L187][AbstractMessageConverterMethodProcessor_L187]         | error(json)        |
| http://localhost:8080/bar/json-a       | some-ex-error.html                                                                      | some-ex-error.html |
| http://localhost:8080/bar/json-b       | Could not find acceptable representation                                                | 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                                                | Could not find acceptable representation |

註意上方表格的``Could not find acceptable representation``錯誤,產生這個的原因和之前**為何curl text/plain資源無法獲得error**是一樣的:
無法將[@ExceptionHandler][spring-ExceptionHandler]返回的數據轉換[@RequestMapping.produces][RequestMapping_produces]所要求的格式。

所以你會發現如果使用[@ExceptionHandler][spring-ExceptionHandler],那就得自己根據請求頭``Accept``的不同而輸出不同的結果了,辦法就是定義一個``void @ExceptionHandler``,具體見[@ExceptionHandler javadoc][spring-ExceptionHandler-javadoc]。

## 定制不同Status Code的錯誤頁面

Spring Boot 官方文檔提供了一種簡單的根據不同Status Code跳到不同error頁面的方法,見[這裏][spring-boot-status-code-error-page]。

我們可以將不同的Status Code的頁面放在`classpath: public/error`或`classpath: templates/error`目錄下,比如`400.html`、`5xx.html`、`400.ftl`、`5xx.ftl`。

打開瀏覽器訪問以下url會獲得不同的結果:

| 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-403`和`loo/error-406`能夠返回我們期望的錯誤頁面,這是為什麽?先來看看代碼。

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

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

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

```java
@ResponseStatus(NOT_ACCEPTABLE)
public class Exception406 extends RuntimeException
```

註意到這兩個異常都有[@ResponseStatus][spring-ResponseStatus-javadoc]註解,這個是註解標明了這個異常所對應的Status Code。
但是在`loo/error-600`中拋出的[SomeException][SomeException]沒有這個註解,而是嘗試在``Response.setStatus(600)``來達到目的,但結果是失敗的,這是為什麽呢?:

```java
@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對於異常的處理機制,下面簡單講解一下:

Spring MVC處理異常的地方在[DispatcherServlet.processHandlerException][DispatcherServlet_L1216],這個方法會利用[HandlerExceptionResolver][spring-HandlerExceptionResolver]來看異常應該返回什麽`ModelAndView`。

目前已知的[HandlerExceptionResolver][spring-HandlerExceptionResolver]有這麽幾個:

1. [DefaultErrorAttributes][spring-DefaultErrorAttributes-javadoc],只負責把異常記錄在Request attributes中,name是`org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.ERROR`
1. [ExceptionHandlerExceptionResolver][spring-ExceptionHandlerExceptionResolver-javadoc],根據[@ExceptionHandler][spring-ExceptionHandler] resolve
1. [ResponseStatusExceptionResolver][spring-ResponseStatusExceptionResolver-javadoc],根據[@ResponseStatus][spring-ResponseStatus-javadoc] resolve
1. [DefaultHandlerExceptionResolver][spring-DefaultHandlerExceptionResolver-javadoc],負責處理Spring MVC標準異常

``Exception403``和``Exception406``都有被[ResponseStatusExceptionResolver][spring-ResponseStatusExceptionResolver-javadoc]處理了,而``SomeException``沒有任何Handler處理,這樣``DispatcherServlet``就會將這個異常往上拋至到容器處理(見[DispatcherServlet#L1243][DispatcherServlet_L1243]),以Tomcat為例,它在[StandardHostValve#L317][StandardHostValve_L317]、[StandardHostValve#L345][StandardHostValve_L345]會將Status Code設置成500,然後跳轉到`/error`,結果就是[BasicErrorController][BasicErrorController]處理時就看到Status Code=500,然後按照500去找error page找不到,就只能返回White error page了。


實際上,從Request的attributes角度來看,交給[BasicErrorController][BasicErrorController]處理時,和容器自己處理時,有幾個相關屬性的內部情況時這樣的:

| 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`

解決辦法有兩個:

1. 給``SomeException``添加``@ResponseStatus``,但是這個方法有兩個局限:
    1. 如果這個異常不是你能修改的,比如在第三方的Jar包裏
    1. 如果``@ResponseStatus``使用[HttpStatus][HttpStatus-javadoc]作為參數,但是這個枚舉定義的Status Code數量有限
1. 使用[@ExceptionHandler][spring-ExceptionHandler],不過得註意自己決定view以及status code


第二種解決辦法的例子`loo/error-601`,對應的代碼:

```java
@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 {
  // 需要設置Status Code,否則響應結果會是200
  response.setStatus(601);
  model.addAllAttributes(errorAttributes.getErrorAttributes(new ServletRequestAttributes(request), true));
  return "error/6xx";
}
```

總結:

1. 沒有被[HandlerExceptionResolver][spring-HandlerExceptionResolver]resolve到的異常會交給容器處理。已知的實現有(按照順序):
    1. [DefaultErrorAttributes][spring-DefaultErrorAttributes-javadoc],只負責把異常記錄在Request attributes中,name是`org.springframework.boot.autoconfigure.web.DefaultErrorAttributes.ERROR`
    1. [ExceptionHandlerExceptionResolver][spring-ExceptionHandlerExceptionResolver-javadoc],根據[@ExceptionHandler][spring-ExceptionHandler] resolve
    1. [ResponseStatusExceptionResolver][spring-ResponseStatusExceptionResolver-javadoc],根據[@ResponseStatus][spring-ResponseStatus-javadoc] resolve
    1. [DefaultHandlerExceptionResolver][spring-DefaultHandlerExceptionResolver-javadoc],負責處理Spring MVC標準異常
1. [@ResponseStatus][spring-ResponseStatus-javadoc]用來規定異常對應的Status Code,其他異常的Status Code由容器決定,在Tomcat裏都認定為500([StandardHostValve#L317][StandardHostValve_L317]、[StandardHostValve#L345][StandardHostValve_L345])
1. [@ExceptionHandler][spring-ExceptionHandler]處理的異常不會經過[BasicErrorController][BasicErrorController],需要自己決定如何返回頁面,並且設置Status Code(如果不設置就是200)
1. [BasicErrorController][BasicErrorController]會嘗試根據Status Code找error page,找不到的話就用Whitelabel error page

本章節代碼在[me.chanjar.boot.customstatuserrorpage][pkg-me.chanjar.boot.customstatuserrorpage],使用[CustomStatusErrorPageExample][boot-CustomStatusErrorPageExample]運行。

## 利用ErrorViewResolver來定制錯誤頁面

前面講到[BasicErrorController][BasicErrorController]會根據Status Code來跳轉對應的error頁面,其實這個工作是由[DefaultErrorViewResolver][DefaultErrorViewResolver-javadoc]完成的。

實際上我們也可以提供自己的[ErrorViewResolver][ErrorViewResolver-javadoc]來定制特定異常的error頁面。

```java
@Component
public class SomeExceptionErrorViewResolver implements ErrorViewResolver {

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

}
```

不過需要註意的是,無法通過[ErrorViewResolver][ErrorViewResolver-javadoc]設定Status Code,Status Code由[@ResponseStatus][spring-ResponseStatus-javadoc]或者容器決定(Tomcat裏一律是500)。

本章節代碼在[me.chanjar.boot.customerrorviewresolver][pkg-me.chanjar.boot.customerrorviewresolver],使用[CustomErrorViewResolverExample][boot-CustomErrorViewResolverExample]運行。


## @ExceptionHandler 和 @ControllerAdvice

前面的例子中已經有了對[@ControllerAdvice][spring-ControllerAdvice]和[@ExceptionHandler][spring-ExceptionHandler]的使用,這裏只是在做一些補充說明:

1. ``@ExceptionHandler``配合``@ControllerAdvice``用時,能夠應用到所有被``@ControllerAdvice``切到的Controller
2. ``@ExceptionHandler``在Controller裏的時候,就只會對那個Controller生效


## 附錄I

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

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


  [spring-boot-doc]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-error-handling
  [spring-mvc-doc]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers
  [blog-exception-handling-in-spring-mvc]: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
  [RequestMapping]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java
  [RequestMappingHandlerMapping]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
  [AbstractHandlerMethodMapping_L341]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java#L341
  [AbstractHandlerMethodMapping_L352]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java#L352 
  [BasicErrorController]: https://github.com/spring-projects/spring-boot/blob/v1.5.4.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java
  [BasicErrorController_errorHtml]: https://github.com/spring-projects/spring-boot/blob/v1.5.4.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java#L86
  [BasicErrorController_error]: https://github.com/spring-projects/spring-boot/blob/v1.5.4.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/BasicErrorController.java#L98
  [RequestMappingInfo_L266]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java#L266
  [ProducesRequestCondition_L235]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java#L235
  [HttpEntityMethodProcessor_L159]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java#L159
  [HttpEntityMethodProcessor]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java
  [AbstractMessageConverterMethodProcessor]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java
  [AbstractMessageConverterMethodProcessor_L259]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java#L259
  [AbstractMessageConverterMethodProcessor_L163]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java#L163
  [AbstractMessageConverterMethodProcessor_L187]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java#L187
  [HttpMessageConverter]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverter.java
  [RequestMapping_produces]: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java#L396
  
  [SomeException]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/exception/SomeException.java
  
  [def-foo]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/controllers/FooController.java
  [def-foo-rest]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/controllers/FooRestController.java
  
  [pkg-me.chanjar.boot.def]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/def
  [boot-CustomDefaultErrorViewConfiguration]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customdefaulterrorview/CustomDefaultErrorViewConfiguration.java
  [boot-DefaultExample]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/def/DefaultExample.java
  
  [pkg-me.chanjar.boot.customdefaulterrorview]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customdefaulterrorview
  [boot-custom-error-page-error-html]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/resources/templates/custom-error-page/error.html
  [boot-CustomDefaultErrorViewExample]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customdefaulterrorview/CustomDefaultErrorViewExample.java
  
  [pkg-me.chanjar.boot.customerrorattributes]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorattributes
  [boot-CustomErrorAttributes]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorattributes/CustomErrorAttributes.java
  [boot-CustomErrorAttributesExample]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorattributes/CustomErrorAttributesExample.java

  [pkg-me.chanjar.boot.customerrorcontroller]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorcontroller
  [boot-CustomErrorController]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorcontroller/CustomErrorController.java
  [boot-CustomErrorControllerConfiguration]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorcontroller/CustomErrorControllerConfiguration.java
  [boot-CustomErrorControllerExample]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorcontroller/CustomErrorControllerExample.java
  
  [pkg-me.chanjar.boot.controlleradvice]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/controlleradvice/
  [boot-BarController]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/controlleradvice/BarController.java
  [boot-BarControllerAdvice]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/controlleradvice/BarControllerAdvice.java
  
  [logback-spring.xml]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/resources/logback-spring.xml
  
  [pkg-me.chanjar.boot.customstatuserrorpage]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customstatuserrorpage
  [boot-CustomStatusErrorPageExample]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customstatuserrorpage/CustomStatusErrorPageExample.java
  
  [pkg-me.chanjar.boot.customerrorviewresolver]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorviewresolver
  [boot-CustomErrorViewResolverExample]: https://github.com/chanjarster/spring-mvc-error-handling-example/blob/master/src/main/java/me/chanjar/boot/customerrorviewresolver/CustomErrorViewResolverExample.java
  
  [spring-ExceptionHandler]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#mvc-ann-exceptionhandler
  [spring-ControllerAdvice]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#mvc-ann-controller-advice
  [spring-ExceptionHandler-javadoc]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html
  [spring-boot-status-code-error-page]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-error-handling-custom-error-pages
  [spring-ResponseStatus-javadoc]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/bind/annotation/ResponseStatus.html
  [spring-HandlerExceptionResolver]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers-resolver
  [spring-DefaultErrorAttributes-javadoc]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/autoconfigure/web/DefaultErrorAttributes.html
  [spring-ExceptionHandlerExceptionResolver-javadoc]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html
  [spring-ResponseStatusExceptionResolver-javadoc]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.html
  [spring-DefaultHandlerExceptionResolver-javadoc]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.html
  [StandardHostValve_L317]: https://github.com/apache/tomcat/blob/TONCAT_9_0_0_M23/java/org/apache/catalina/core/StandardHostValve.java#L317
  [StandardHostValve_L345]: https://github.com/apache/tomcat/blob/TONCAT_9_0_0_M23/java/org/apache/catalina/core/StandardHostValve.java#L345
  [DispatcherServlet_L1216]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L1216
  [DispatcherServlet_L1243]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L1243
  [HttpStatus-javadoc]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/http/HttpStatus.html
  [DefaultErrorViewResolver-javadoc]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/autoconfigure/web/DefaultErrorViewResolver.html
  [ErrorViewResolver-javadoc]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/autoconfigure/web/ErrorViewResolver.html

Tags: quot localhost Spring 8080 http 返回

文章來源:


ads
ads

相關文章
ads

相關文章

ad