之前在使用springmvc的時(shí)候可以通過(guò)在web.xml中配置404指向?qū)?yīng)的頁(yè)面狸棍,但是在spring boot是沒(méi)有web.xml的,如果沒(méi)有任何配置的話我們會(huì)得到下面的返回
那么我想spring boot肯定為我們準(zhǔn)備好了對(duì)應(yīng)的解決方案狞贱,那就是BasicErrorController,這個(gè)繼承自AbstractErrorController蜀涨,而且是在沒(méi)有其他實(shí)現(xiàn)的時(shí)候瞎嬉,這個(gè)默認(rèn)的ErrorController才會(huì)生效,我們可以看到ErrorMvcAutoConfiguration里面的聲明厚柳,里面的ConditionalOnMissingBean就是這個(gè)意思
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
this.errorViewResolvers);
}
下面我們關(guān)注一下BasicErrorController里面的兩個(gè)方法氧枣,一個(gè)方法對(duì)面的是我們上面瀏覽器訪問(wèn)的的那個(gè)返回,一個(gè)application/json的post請(qǐng)求返回别垮,他們返回了自己預(yù)先定義的數(shù)據(jù)便监,這里我們注意下一個(gè)細(xì)節(jié),就是ModelAndView modelAndView = resolveErrorView(request, response, status, model)這一行碳想,我們進(jìn)去看下他的源碼烧董,就會(huì)知道,這里去拿返回的時(shí)候胧奔,它會(huì)默認(rèn)去項(xiàng)目的資源目錄根據(jù)請(qǐng)求的狀態(tài)碼獲取html文件返回逊移,所以如果只支持瀏覽器訪問(wèn)的get方式的話,我們直接定義好對(duì)應(yīng)的html文件就可以了龙填,spring boot靜態(tài)默認(rèn)的默認(rèn)目錄是static胳泉,這里去error下去找view,那么只要在/resources/static/error/目錄下定義文件就行源碼image.png
@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);
}
但是我們更多的是接口請(qǐng)求岩遗,而且這樣靈活性太差胶背,所以我們還是自己定義一個(gè)ErrorController比較合適,然后重寫(xiě)這兩個(gè)方法喘先,errorHtml這個(gè)基本和BasicErrorController一樣,只要定義好靜態(tài)文件就行廷粒,對(duì)于error方法窘拯,我們返回自己系統(tǒng)定義的json格式數(shù)據(jù)红且,下面是具體實(shí)現(xiàn),類的其他代碼直接參考BasicErrorController的就行涤姊,定義完自己的ExceptionController之后暇番,還需要一個(gè)配置類的
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
log.error("接口請(qǐng)求錯(cuò)誤,http status:{}", status);
Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
JsonResult<Object> result = new JsonResult<>(ResultCode.NOT_FOUND);
model.put("restResponse", result);
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
}
@RequestMapping
@ResponseBody
public ResponseEntity<JsonResult<String>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
log.error("接口請(qǐng)求錯(cuò)誤思喊,http status:{}", status);
//這里構(gòu)建自己的輸出格式,詳細(xì)代碼就不貼出,如有需要可以到代碼倉(cāng)庫(kù)查看
if (Objects.equals(status.value(), 404)) {
return new ResponseEntity<>(new JsonResult<>(ResultCode.NOT_FOUND), status);
} else {
return new ResponseEntity<>(new JsonResult<>(ResultCode.ERROR_SYSTEM), status);
}
}
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(ResourceProperties.class)
public class ExceptionControllerConfig {
private final ServerProperties serverProperties;
private final List<ErrorViewResolver> errorViewResolvers;
public ExceptionControllerConfig(ServerProperties serverProperties,
ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
this.serverProperties = serverProperties;
this.errorViewResolvers = errorViewResolversProvider.getIfAvailable();
}
@Bean
public ExceptionController exceptionController(ErrorAttributes errorAttributes) {
return new ExceptionController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}
}
這樣我們就定義好了自己的錯(cuò)誤控制器壁酬,可以根據(jù)不同的請(qǐng)求狀態(tài)碼返回自己的數(shù)據(jù);
還有一種做法
還有一種做法就是通過(guò)spring boot的配置直接完成恨课,這樣配置的顧名思義舆乔,就是在沒(méi)有handler的時(shí)候直接作為異常拋出,這樣我們的全局異常捕獲器就能夠捕獲到了剂公,然后根據(jù)不同的狀態(tài)碼返回就行希俩,但是這里需要看到的是有個(gè)add-mappings:false,就是這種情況下靜態(tài)資源是要不能訪問(wèn)的纲辽,其實(shí)是因?yàn)殪o態(tài)資源是直接訪問(wèn)颜武,不需要控制器,所以要配置成false拖吼;如果你的項(xiàng)目?jī)H僅是作為接口服務(wù)的話鳞上,那么這種方式來(lái)的更加的簡(jiǎn)單,我本來(lái)也是這么做的吊档,但是我的項(xiàng)目里面使用了swagger篙议,這個(gè)東西暴露出去的api是動(dòng)態(tài)生成的靜態(tài)文件,如果這樣配置之后籍铁,swagger也不能用了涡上,所以我還是使用了第一種方式進(jìn)行捕獲error
spring:
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false