1. 使用 @ControllerAdvice 和 @ExceptionHandler 处理全局异常
①新建异常实体类
publicclassErrorResponse{privateString message;privateString errorTypeName;publicErrorResponse(Exception e){this(e.getClass().getName(), e.getMessage());}publicErrorResponse(String errorTypeName,String message){this.errorTypeName= errorTypeName;this.message= message;}......省略getter/setter方法}
②自定义异常类型
一般我们处理的都是 RuntimeException ,所以如果你需要自定义异常类型的话直接集成这个类就可以了。
publicclassResourceNotFoundExceptionextendsRuntimeException{privateString message;publicResourceNotFoundException(){super();}publicResourceNotFoundException(String message){super(message);this.message= message;}@OverridepublicStringgetMessage(){return message;}publicvoidsetMessage(String message){this.message= message;}}
③新建异常处理类
我们只需要在类上加上@ControllerAdvice注解这个类就成为了全局异常处理类,当然你也可以通过 assignableTypes指定特定的 Controller 类,让异常处理类只处理特定类抛出的异常。
@ControllerAdvice(assignableTypes={ExceptionController.class})@ResponseBodypublicclassGlobalExceptionHandler{ErrorResponse illegalArgumentResponse=newErrorResponse(newIllegalArgumentException("参数错误!"));ErrorResponse resourseNotFoundResponse=newErrorResponse(newResourceNotFoundException("Sorry, the resourse not found!"));@ExceptionHandler(value=Exception.class)// 拦截所有异常, 这里只是为了演示,一般情况下一个方法特定处理一种异常publicResponseEntity<ErrorResponse>exceptionHandler(Exception e){if(einstanceofIllegalArgumentException){returnResponseEntity.status(400).body(illegalArgumentResponse);}elseif(einstanceofResourceNotFoundException){returnResponseEntity.status(404).body(resourseNotFoundResponse);}returnnull;}}
ResponseEntity 可以定义返回的HttpStatus(状态码)和HttpHeaders(消息头:请求头和响应头)。
ResponseEntity的优先级高于@ResponseBody。
在不是ResponseEntity的情况下才去检查有没有@ResponseBody注解。如果响应类型是ResponseEntity可以不写@ResponseBody注解,写了也没有关系。
简单的说
@ResponseBody可以直接返回Json结果,
@ResponseEntity不仅可以返回json结果,还可以定义返回的HttpHeaders和HttpStatus。
④controller模拟抛出异常
@RestController@RequestMapping("/api")publicclassExceptionController{@GetMapping("/illegalArgumentException")publicvoidthrowException(){thrownewIllegalArgumentException();}@GetMapping("/resourceNotFoundException")publicvoidthrowException2(){thrownewResourceNotFoundException();}}
2.@ExceptionHandler 处理 Controller 级别的异常(使用的比较少)
@ExceptionHandler(value=Exception.class)// 拦截所有异常publicResponseEntity<ErrorResponse>exceptionHandler(Exception e){if(einstanceofIllegalArgumentException){returnResponseEntity.status(400).body(illegalArgumentResponse);}elseif(einstanceofResourceNotFoundException){returnResponseEntity.status(404).body(resourseNotFoundResponse);}returnnull;}
3.ResponseStatusException
通过 ResponseStatus注解简单处理异常的方法(将异常映射为状态码)。
ResponseStatusException 提供了三个构造方法:
publicResponseStatusException(HttpStatus status){this(status,null,null);}publicResponseStatusException(HttpStatus status,@NullableString reason){this(status, reason,null);}publicResponseStatusException(HttpStatus status,@NullableString reason,@NullableThrowable cause){super(null, cause);Assert.notNull(status,"HttpStatus is required");this.status= status;this.reason= reason;}
@GetMapping("/resourceNotFoundException2")publicvoidthrowException3(){thrownewResponseStatusException(HttpStatus.NOT_FOUND,"Sorry, the resourse not found!",newResourceNotFoundException());}
4.实现WebMvcConfigurer 接口,增加异常处理器
importcom.alibaba.fastjson.JSON;importcom.scaffold.test.base.Result;importcom.scaffold.test.base.ResultCode;importcom.scaffold.test.base.ServiceException;importcom.scaffold.test.config.interceptor.AuthenticationInterceptor;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.validation.BindingResult;importorg.springframework.validation.ObjectError;importorg.springframework.web.bind.MethodArgumentNotValidException;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerExceptionResolver;importorg.springframework.web.servlet.ModelAndView;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.util.List;/**
* @author alex
*/@ConfigurationpublicclassWebMvcConfigimplementsWebMvcConfigurer{privatefinalLogger logger=LoggerFactory.getLogger(WebMvcConfigurer.class);/**
* 统一异常处理
*
* @param exceptionResolvers
*/@OverridepublicvoidconfigureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers){
exceptionResolvers.add((request, response, handler, e)->{Result result=newResult();// 异常处理// 参数异常判断if(einstanceofBindingResult|| einstanceofMethodArgumentNotValidException){StringBuilder errorMessage=newStringBuilder();List<ObjectError> allErrors;if(einstanceofBindingResult){
allErrors=((BindingResult) e).getAllErrors();}else{BindingResult bindingResult=((MethodArgumentNotValidException) e).getBindingResult();
allErrors= bindingResult.getAllErrors();}for(int i=0; i< allErrors.size(); i++){
errorMessage.append(allErrors.get(i).getDefaultMessage());if(i!= allErrors.size()-1){
errorMessage.append(",");}}
result.setCode(ResultCode.FAIL).setMessage(errorMessage.toString());
logger.error(errorMessage.toString());}elseif(einstanceofServiceException){// 1、业务失败的异常,如“账号或密码错误”
result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
logger.info(e.getMessage());}elseif(einstanceofServletException){// 2、调用失败
result.setCode(ResultCode.FAIL).setMessage(e.getMessage());}else{// 3、内部其他错误
result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 ["+ request.getRequestURI()+"] 内部错误,请联系管理员");String message;if(handlerinstanceofHandlerMethod){HandlerMethod handlerMethod=(HandlerMethod) handler;
message=String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
request.getRequestURI(),
handlerMethod.getBean().getClass().getName(),
handlerMethod.getMethod().getName(),
e.getMessage());}else{
message= e.getMessage();}
result.setMessage(message);
logger.error(message, e);}responseResult(response, result);returnnewModelAndView();});}// 处理响应数据格式privatevoidresponseResult(HttpServletResponse response,Result result){
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-type","application/json;charset=UTF-8");
response.setStatus(200);try{
response.getWriter().write(JSON.toJSONString(result));}catch(IOException ex){
logger.error(ex.getMessage());}}}