在一個(gè)web項(xiàng)目開發(fā)中剪验,通常都會(huì)涉及到Html和Json請(qǐng)求毯欣。當(dāng)出現(xiàn)異常的時(shí)候燎孟,我們需要根據(jù)請(qǐng)求類型返回不同的信息。如果是Json請(qǐng)求,那么就返回
String
或者ReponseEntity
類型岸裙;如果是html請(qǐng)求猖败,就要返回ModelAndView
的錯(cuò)誤頁面。
我們當(dāng)然可以對(duì)Controller
的每個(gè)接口方法拋出的異常單獨(dú)處理降允。但這樣做會(huì)導(dǎo)致大量的重復(fù)工作恩闻。Spring MVC為我們提供了@ControllerAdvice
和@ExceptionHandler`兩個(gè)注解來實(shí)現(xiàn)全局的異常處理。關(guān)于這兩個(gè)注解的用法可以參考這里剧董。
這解決了大部分的問題幢尚,但是如果同一個(gè)Controller
中既有html又有json接口方法怎么辦呢?我們當(dāng)然可以拆分成兩個(gè)Controller
翅楼,一個(gè)包含html接口尉剩,另一個(gè)包含json接口。但是這樣做不夠靈活毅臊,而且我更習(xí)慣根據(jù)業(yè)務(wù)邏輯來歸類接口理茎。有沒有更好的方法呢?
其實(shí)在寫異常處理方法時(shí)管嬉,我們可以將請(qǐng)求信息作為參數(shù)傳入功蜓,并根據(jù)請(qǐng)求類型來返回不同的數(shù)據(jù)。
public class BaseController{
private Boolean isJson(HttpServletRequest request){
String header = request.getHeader("content-type");
return header != null && header.contains("json");
}
@Override
@ExceptionHandler(BaseException.class)
public Object handleBaseException(HttpServletRequest request, baseException e) {
if(isJson(request)) {
return ResponseUtils.restResponse(
e.getCode(),
e.getMessage(),
e.getStatus()
);
} else {
ModelAndView modelAndView = initModelAndView();
if (e.getCode().equalsIgnoreCase("login_first")) {
modelAndView.setViewName("redirect:/list");
}
if (e.getCode().equalsIgnoreCase("real_name_not_set")) {
modelAndView.setViewName("redirect:/account");
}else{
modelAndView.setViewName("/404");
}
modelAndView.addObject("exception", e);
return modelAndView;
}
}
}
這里我們寫了一個(gè)BaseController
宠蚂,并在Controller
中實(shí)現(xiàn)了異常捕獲的邏輯式撼。isJson()
通過判斷請(qǐng)求的Content-Type
是否包含json字符串來判斷該請(qǐng)求類型。當(dāng)然求厕,更好更合適的方式是通過包頭中的Accept
中的信息類判斷著隆。需要注意的是handleBaseException()
方法返回了Object類型,這樣我們就可以根據(jù)需要返回不同類型的數(shù)據(jù)了呀癣。以后只要Contrller
繼承BaseController
就不用再考慮異常的問題了美浦。
但是,如果異常是在進(jìn)入接口方法之前被拋出的呢项栏。比如404浦辨,406錯(cuò)誤,根本不會(huì)執(zhí)行接口方法沼沈,因此也無法被ExceptionHandler
捕獲流酬。這部分異常如何處理呢?
Spring Boot提供了一個(gè)統(tǒng)一的/error
地址用于所有未被捕獲的異常拋出列另。默認(rèn)設(shè)置下顯示的是一個(gè)whitelabel error page
芽腾。
通過實(shí)現(xiàn)ErrorController,我們可以定制這個(gè)錯(cuò)誤頁面页衙。
@Controller
public class MpErrorController extends BaseController implements ErrorController {
private static final String PATH = "/error";
@RequestMapping(value = PATH)
public Object error() throws Exception {
throw new BaseException();
}
@Override
public String getErrorPath() {
return PATH;
}
}
我們希望這個(gè)error頁面也根據(jù)請(qǐng)求的類型做出不同的邏輯處理摊滔。因此阴绢,可以直接在error()
拋出BaseException異常,并且讓這個(gè)Controller繼承于BaseController艰躺。這樣呻袭,被拋出的異常也會(huì)被handleBaseException()
捕獲了。
至此腺兴,我們比較優(yōu)雅的實(shí)現(xiàn)了全局的異常處理棒妨。所有的BaseException
異常處理邏輯都集中在handleBaseException()
方法中。