SpringBoot初始教程之統一異常處理(三)
SpringBoot初始教程之統一異常處理(三)
1.介紹
在日常開發中發生了異常,往往是需要通過一個統一的異常處理處理所有異常,來保證客戶端能夠收到友好的提示。SpringBoot在頁面
發生異常的時候會自動把請求轉到/error,SpringBoot內建了一個BasicErrorController
對異常進行統一的處理,當然也可以自定義這個路徑
application.yaml
server:
port: 8080
error:
path: /custom/error
- 1
- 2
- 3
- 4
- 5
- 6
BasicErrorController
@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()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView == null ? new ModelAndView("error", model) : modelAndView); } @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); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
分別會有如下兩種返回
{
"timestamp": 1478571808052,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/rpc"
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
2.通用Exception處理
通過使用@ControllerAdvice來進行統一異常處理,@ExceptionHandler(value = Exception.class)
來指定捕獲的異常
下面針對兩種異常進行了特殊處理分別返回頁面和json資料,使用這種方式有個侷限,無法根據不同的頭部返回不同的資料格式,而且無法針對404、403等多種狀態進行處理
@ControllerAdvice
public class GlobalExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = CustomException.class)
@ResponseBody
public ResponseEntity defaultErrorHandler(HttpServletRequest req, CustomException e) throws Exception {
return ResponseEntity.ok("ok");
}
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3.自定義BasicErrorController
錯誤處理
在初始介紹哪裡提到了BasicErrorController
,這個是SpringBoot的預設錯誤處理,也是一種全域性處理方式。咱們可以模仿這種處理方式自定義自己的全域性錯誤處理
下面定義了一個自己的BasicErrorController,可以根據自己的需求自定義errorHtml()
和error()
的返回值。
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
private static final Logger LOGGER = LoggerFactory.getLogger(BasicErrorController.class);
@Autowired
private ApplicationContext applicationContext;
/**
* Create a new {@link org.springframework.boot.autoconfigure.web.BasicErrorController} instance.
*
* @param errorAttributes the error attributes
* @param errorProperties configuration properties
*/
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties) {
this(errorAttributes, errorProperties,
Collections.<ErrorViewResolver>emptyList());
}
/**
* Create a new {@link org.springframework.boot.autoconfigure.web.BasicErrorController} instance.
*
* @param errorAttributes the error attributes
* @param errorProperties configuration properties
* @param errorViewResolvers error view resolvers
*/
public BasicErrorController(ErrorAttributes errorAttributes,
ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
@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());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
insertError(request);
return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
}
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
insertError(request);
return new ResponseEntity(body, status);
}
/**
* Determine if the stacktrace attribute should be included.
*
* @param request the source request
* @param produces the media type produced (or {@code MediaType.ALL})
* @return if the stacktrace attribute should be included
*/
protected boolean isIncludeStackTrace(HttpServletRequest request,
MediaType produces) {
ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
return true;
}
if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
/**
* Provide access to the error properties.
*
* @return the error properties
*/
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
SpringBoot提供了一種特殊的Bean定義方式,可以讓我們容易的覆蓋已經定義好的Controller,原生的BasicErrorController
是定義在ErrorMvcAutoConfiguration
中的
具體程式碼如下:
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
this.errorViewResolvers);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
可以看到這個註解@ConditionalOnMissingBean
意思就是定義這個bean 當 ErrorController.class
這個沒有定義的時候,
意思就是說只要我們在程式碼裡面定義了自己的ErrorController.class
時,這段程式碼就不生效了,具體自定義如下:
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(ResourceProperties.class)
public class ConfigSpringboot {
@Autowired(required = false)
private List<ErrorViewResolver> errorViewResolvers;
private final ServerProperties serverProperties;
public ConfigSpringboot(
ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
@Bean
public MyBasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new MyBasicErrorController(errorAttributes, this.serverProperties.getError(),
this.errorViewResolvers);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
在使用的時候需要注意MyBasicErrorController
不能被自定義掃描Controller掃描到,否則無法啟動。
3.總結
一般來說自定義BasicErrorController這種方式比較實用,因為可以通過不同的頭部返回不同的資料格式,在配置上稍微複雜一些,但是從實用的角度來說比較方便而且可以定義通用元件