sprint-boot錯誤處理+錯誤處理的自動配置+定製錯誤響應
阿新 • • 發佈:2018-12-16
1瀏覽器訪問,返回錯誤給瀏覽器
2客戶端訪問,返回json給客戶端
1錯誤處理的自動配置
ErrorMvcAutoConfiguration
步驟:
1 一但系統出現4xx或者5xx之類的錯誤,ErrorPageCustomizer就會生效(定製錯誤的響應規則)
相當於發生錯誤後,返回/error請求,這裡會重定向 private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { private final ServerProperties properties; private final DispatcherServletPath dispatcherServletPath; protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) { this.properties = properties; this.dispatcherServletPath = dispatcherServletPath; } @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath .getRelativePath(this.properties.getError().getPath()));//返回的是/error errorPageRegistry.addErrorPages(errorPage); } @Override public int getOrder() { return 0; } }
2 就會發生/error請求
3 就會被BasicErrorController處理。
@Bean @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT) public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) { return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers); }@Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController {//處理/error請求@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)//處理瀏覽器的錯誤,返回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) ? modelAndView : new ModelAndView("error", model); } @RequestMapping //處理客戶端錯誤,返回json 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<>(body, status); }
4 DefaultErrorViewResolver決定去哪個頁面
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered { private static final Map<Series, String> SERIES_VIEWS; static { Map<Series, String> views = new EnumMap<>(Series.class); //處理4開頭的狀態碼 views.put(Series.CLIENT_ERROR, "4xx"); views.put(Series.SERVER_ERROR, "5xx"); SERIES_VIEWS = Collections.unmodifiableMap(views); }決定去哪個頁面 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); } private ModelAndView resolveResource(String viewName, Map<String, Object> model) { for (String location : this.resourceProperties.getStaticLocations()) { try { Resource resource = this.applicationContext.getResource(location); resource = resource.createRelative(viewName + ".html"); if (resource.exists()) { return new ModelAndView(new HtmlResourceView(resource), model); } } catch (Exception ex) { } } return null; }
什麼程式碼決定有模板引擎情況下,會去/error下狀態碼對應的頁面。
如果沒有模板引擎就去找靜態資原始檔夾下狀態碼對應的頁面
如果沒有狀態碼對應的錯誤頁面,就去系統提供的預設頁面
2定製錯誤響應
1相應都是json資料
測試程式碼
@ControllerAdvice
public class MyExceptionHandler {
//1、瀏覽器客戶端返回的都是json
@ResponseBody
@ExceptionHandler(Exception.class)
public Map<String,Object> handleException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("code","500");
map.put("message","500錯誤");
return map;
}
}
@Controller
public class HelloController {
@RequestMapping("/hello")
public void hello(){
throw new RuntimeException("RuntimeException");
}
}
2根據客戶端和瀏覽器來返回不同的方式
@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(Exception.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); //設定錯誤狀態碼 request.setAttribute("javax.servlet.error.status_code",520); map.put("code","520"); map.put("message","520錯誤"); request.setAttribute("ext",map); //轉發到/error,那裡會到瀏覽器和客戶端情況分別進行設定 return "forward:/error"; } }
圖中一個是5xx頁面
3定製返回資料
在原有基礎上新增類
//給容器中加入我們自己定義的ErrorAttributes @Component public class MyErrorAttributes extends DefaultErrorAttributes { @Override //返回值的map就是頁面和json能獲取的所有欄位 public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace); map.put("message1","message1"); map.put("message2","message2"); map.put("message3","message3"); //我們的異常處理器攜帶的資料 Map<String,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0); map.put("ext",ext); return map; } }