1) springboot默認的錯誤處理機制
默認效果:
1) 瀏覽器, 返回一個默認的錯誤頁面
2) 如果是其他客戶端,默認響應一個json數(shù)據(jù).
原理:
可以參照ErrorMvcAutoConfiguration:錯誤處理的自動配置
a. DefaultErrorAttributes
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, webRequest);
addErrorDetails(errorAttributes, webRequest, includeStackTrace);
addPath(errorAttributes, webRequest);
return errorAttributes;
}
b. BasicErrorController:處理/error請求
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
// 產(chǎn)生html類型的數(shù)據(jù);瀏覽器發(fā)送的請求來到這處理
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
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());
//去哪個頁面作為錯誤頁面,包含頁面地址和頁面內(nèi)容
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// 產(chǎn)生json數(shù)據(jù)
@RequestMapping
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);
}
c. ErrorPageCustomizer
public class ErrorProperties {
// 系統(tǒng)出現(xiàn)錯誤后來到error請求進行處理遗遵;(web.xml注冊的錯誤頁面規(guī)則)
/**
* Path of the error controller.
*/
@Value("${error.path:/error}")
private String path = "/error";
d. DefaultErrorViewResolver
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()), 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) {
// 默認springboot可以去找到一個頁面躺同, error/404
String errorViewName = "error/" + viewName;
// 模板引擎可以解析這個頁面地址就用模板引擎解析
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext);
if (provider != null) {
// 模板引擎可用的情況下返回到errorVIewName指定的視圖地址
return new ModelAndView(errorViewName, model);
}
// 模板引擎不可用,就在靜態(tài)資源文件夾下找errorViewName對應的頁面
return resolveResource(errorViewName, model);
}
步驟:
一旦系統(tǒng)出現(xiàn)4xx或者5xx的錯誤: ErrorPageCustomizer生效(定制錯誤的響應規(guī)則)拷恨,就會來到/error請求舔亭, BasicErrorController進行處理些膨,
- 響應頁面:去哪個頁面是由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;
}
2) 如何定制錯誤響應
-
如何定制錯誤頁面
1) 有模板引擎情況,error/404.html [將錯誤頁面命名為錯誤狀態(tài)碼.html放在模板引擎文件里的error文件夾下]钦铺,發(fā)生此狀態(tài)碼的錯誤就會來到對應的頁面.
我們可以使用4xx和5xx命名的錯誤頁面來匹配這種類型的錯誤订雾,精確優(yōu)先(優(yōu)先尋找精確的狀態(tài)碼.html)
可以獲取到的信息:
timestamp:時間戳
status:狀態(tài)碼
error:錯誤提示
exception:異常
message:異常信息
errors:JSR303數(shù)據(jù)校驗的錯誤都在這里2) 沒有模板引擎,去靜態(tài)資源下尋找
3) 以上都沒有矛洞,來到springboot的默認空白頁面
- 如何定制錯誤的json數(shù)據(jù)
a. 自定義異常處理洼哎,返回定制的json數(shù)據(jù)
@ResponseBody
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
public Map<String, Object> handlerException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("codes","user.notExist");
map.put("message",e.getMessage());
return map;
}
}
// 沒有自適應效果,不管是其他客戶端還是瀏覽器都是返回json數(shù)據(jù)
b. 轉(zhuǎn)發(fā)到/error進行自適應響應效果處理
@ExceptionHandler(UserNotExistException.class)
public String handlerException(Exception e, HttpServletRequest req) {
Map<String, Object> map = new HashMap<>();
// 傳入自己的狀態(tài)嗎,
req.setAttribute("javax.servlet.error.status_code",500);
map.put("codes", "用戶出錯了");
map.put("message", e.getMessage());
return "forward:/error";
}
c. 將我們的定制數(shù)據(jù)攜帶出去
出現(xiàn)錯誤以后噩峦,會來到/error請求锭沟,會被BasicErrorController處理,響應出去可以獲取的數(shù)據(jù)识补,是由getErrorAttributes得到的(是由AbstractErrorController(ErrorController)規(guī)定的方法)
- 完全編寫一個ErrorController的實現(xiàn)類[或者是編寫AbstractErrorController的子類]族淮,放在容器中
- 第二種,頁面上能用的數(shù)據(jù)李请,或者是json返回能用的數(shù)據(jù)都是通過errorAttributes得到;容器中DefaultErrorAttributes默認進行數(shù)據(jù)處理的.
自定義
@ExceptionHandler(UserNotExistException.class)
public String handlerException(Exception e, HttpServletRequest req) {
Map<String, Object> map = new HashMap<>();
// 傳入自己的狀態(tài)嗎厉熟,
req.setAttribute("javax.servlet.error.status_code",500);
map.put("codes", "用戶出錯了");
map.put("message", e.getMessage());
req.setAttribute("mm",map);
return "forward:/error";
}
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest request, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes( request,includeStackTrace);
map.put("say","靳飛虎导盅,大笨蛋");
Map<String,Object> mm = (Map) request.getAttribute("mm", 0);
map.put("message",mm);
return map;
}
}
最終的效果,響應是自適應的揍瑟,可以通過定制ErrorAttributes改變需要返回的內(nèi)容.