其他分享
首页 > 其他分享> > 全局异常处理及自定义异常:ErrorController与@ControllerAdvice区别和用法

全局异常处理及自定义异常:ErrorController与@ControllerAdvice区别和用法

作者:互联网

https://blog.csdn.net/jwf111/article/details/88571067

ErrorController

在springboot项目中当我们访问一个不存在的url时经常会出现以下页面

在postman访问时则是以下情况

image

image

对于上面的情况究竟是什么原因造成呢,实际上当springboot项目出现异常时,默认会跳转到/error,而/error则是由BasicErrorController进行处理,其代码如下

  1.   @Controller
  2.   @RequestMapping({"${server.error.path:${error.path:/error}}"})
  3.   public class BasicErrorController extends AbstractErrorController {
  4.       private final ErrorProperties errorProperties;
  5.    
  6.       public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
  7.           this(errorAttributes, errorProperties, Collections.emptyList());
  8.       }
  9.    
  10.       public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
  11.           super(errorAttributes, errorViewResolvers);
  12.           Assert.notNull(errorProperties, "ErrorProperties must not be null");
  13.           this.errorProperties = errorProperties;
  14.       }
  15.    
  16.       public String getErrorPath() {
  17.           return this.errorProperties.getPath();
  18.       }
  19.    
  20.       @RequestMapping(
  21.           produces = {"text/html"}
  22.       )
  23.       public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
  24.           HttpStatus status = this.getStatus(request);
  25.           Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
  26.           response.setStatus(status.value());
  27.           ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
  28.           return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
  29.       }
  30.    
  31.       @RequestMapping
  32.       @ResponseBody
  33.       public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
  34.           Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
  35.           HttpStatus status = this.getStatus(request);
  36.           return new ResponseEntity(body, status);
  37.       }
  38.    
  39.       protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
  40.           IncludeStacktrace include = this.getErrorProperties().getIncludeStacktrace();
  41.           if (include == IncludeStacktrace.ALWAYS) {
  42.               return true;
  43.           } else {
  44.               return include == IncludeStacktrace.ON_TRACE_PARAM ? this.getTraceParameter(request) : false;
  45.           }
  46.       }
  47.    
  48.       protected ErrorProperties getErrorProperties() {
  49.           return this.errorProperties;
  50.       }
  51.   }

下面是自己写的一个ErrorController

  1.   import java.util.HashMap;
  2.   import java.util.Map;
  3.    
  4.   import javax.servlet.http.HttpServletRequest;
  5.   import javax.servlet.http.HttpServletResponse;
  6.    
  7.   import com.alibaba.fastjson.JSONObject;
  8.   import com.xuecheng.framework.model.response.ErrorCode;
  9.   import com.xuecheng.framework.model.response.ResultCode;
  10.   import org.springframework.beans.factory.annotation.Autowired;
  11.   import org.springframework.boot.web.servlet.error.ErrorAttributes;
  12.   import org.springframework.boot.web.servlet.error.ErrorController;
  13.   import org.springframework.stereotype.Controller;
  14.   import org.springframework.web.bind.annotation.RequestMapping;
  15.   /**
  16.    * web错误 全局处理
  17.    * @author jiangwf
  18.    *
  19.    */
  20.   import org.springframework.web.bind.annotation.ResponseBody;
  21.   import org.springframework.web.context.request.ServletWebRequest;
  22.    
  23.   @Controller
  24.   public class InterfaceErrorController implements ErrorController {
  25.       private static final String ERROR_PATH="/error";
  26.       private ErrorAttributes errorAttributes;
  27.    
  28.       @Override
  29.       public String getErrorPath() {
  30.           return ERROR_PATH;
  31.       }
  32.       @Autowired
  33.       public InterfaceErrorController(ErrorAttributes errorAttributes) {
  34.           this.errorAttributes=errorAttributes;
  35.       }
  36.    
  37.       /**
  38.        * web页面错误处理
  39.        */
  40.       @RequestMapping(value=ERROR_PATH,produces="text/html")
  41.       @ResponseBody
  42.       public String errorPageHandler(HttpServletRequest request,HttpServletResponse response) {
  43.           ServletWebRequest requestAttributes =  new ServletWebRequest(request);
  44.           Map<String, Object> attr = this.errorAttributes.getErrorAttributes(requestAttributes, false);
  45.           JSONObject jsonObject = new JSONObject();
  46.           ErrorCode errorCode = new ErrorCode(false, (int) attr.get("status"), (String) attr.get("message"));
  47.           return JSONObject.toJSONString(errorCode);
  48.       }
  49.    
  50.       /**
  51.        * 除web页面外的错误处理,比如json/xml等
  52.        */
  53.       @RequestMapping(value=ERROR_PATH)
  54.       @ResponseBody
  55.       public ResultCode errorApiHander(HttpServletRequest request) {
  56.           ServletWebRequest requestAttributes = new ServletWebRequest(request);
  57.           Map<String, Object> attr=this.errorAttributes.getErrorAttributes(requestAttributes, false);
  58.           return new ErrorCode(false, (int)attr.get("status"), (String) attr.get("message"));
  59.       }
  60.    
  61.   }
  1.   import lombok.AllArgsConstructor;
  2.   import lombok.Data;
  3.   import lombok.ToString;
  4.    
  5.   @ToString
  6.   @Data
  7.   @AllArgsConstructor
  8.   public class ErrorCode implements ResultCode{
  9.    
  10.       private boolean success;
  11.    
  12.       private int code;
  13.    
  14.       private String message;
  15.    
  16.    
  17.       @Override
  18.       public boolean success() {
  19.           return false;
  20.       }
  21.    
  22.       @Override
  23.       public int code() {
  24.           return 0;
  25.       }
  26.    
  27.       @Override
  28.       public String message() {
  29.           return null;
  30.       }
  31.   }
  1.   public interface ResultCode {
  2.       //操作是否成功,true为成功,false操作失败
  3.       boolean success();
  4.       //操作代码
  5.       int code();
  6.       //提示信息
  7.       String message();
  8.    
  9.   }

@ControllerAdvice

 

问题:

 

 

解决方法:

 

异常处理流程

系统对异常的处理使用统一的异常处理流程:

  1. 自定义异常类型
  2. 自定义错误代码及错误信息
  3. 对于可预知的异常由程序员在代码中主动抛出,有SpringMVC统一捕获
    可预知异常是程序员在代码中手动抛出本系统定义的特点异常类型,由于是程序员抛出的异常,通常异常信息比较齐全,程序员在抛出时会指定错误代码及错误信息,获取异常信息也比较方便
  4. 对于不可预知的异常(运行时异常)由SpringMVC统一捕获Exception类型的异常
    不可预知的异常通常是由于系统出现bug、或一些不可抗拒的错误(比如网络中断、服务器宕机等),异常类型为RuntimeException类型(运行时异常)
  5. 可预知异常及不可预知异常最终都会采用统一的信息格式(错误代码+错误信息)来表示,最终也会随请求响应给客户端

异常抛出及处理流程

image

image

  1. 在controller、service、dao中程序员抛出自定义异常;SpringMVC框架抛出框架异常类型
  2. 统一由异常捕获类捕获异常并进行处理
  3. 捕获到自定义异常则直接取出错误代码及错误信息,响应给用户
  4. 捕获到非自定义异常类型首先从Map中找该异常类型是否对应具体的错误代码,如果有则取出错误代码和错误信息并响应给用户,如果从Map中占不到异常类型所对应的错误代码则统一为99999错误代码并响应给用户
  5. 将错误代码及错误信息以json格式响应给用户

下面就开始我们的异常处理编程

一、可预知异常

  1. 自定义异常类
  1.   import com.xuecheng.framework.model.response.ResultCode;
  2.   import jdk.nashorn.internal.objects.annotations.Getter;
  3.    
  4.   /**
  5.    * @Author: jiangweifan
  6.    * @Date: 2019/3/4 20:06
  7.    * @Description:
  8.    */
  9.   public class CustomException extends RuntimeException {
  10.    
  11.       private ResultCode resultCode;
  12.    
  13.       public CustomException(ResultCode resultCode) {
  14.           super("错误代码:" + resultCode.code()+" 错误信息:" + resultCode.message());
  15.           this.resultCode = resultCode;
  16.       }
  17.    
  18.       public ResultCode getResultCode() {
  19.           return resultCode;
  20.       }
  21.   }
  1. 自定义异常抛出类
  1.   import com.xuecheng.framework.model.response.ResultCode;
  2.    
  3.   /**
  4.    * @Author: jiangweifan
  5.    * @Date: 2019/3/4 20:09
  6.    * @Description:
  7.    */
  8.   public class ExceptionCast {
  9.    
  10.       public static void cast(ResultCode resultCode, boolean condition) {
  11.           if (condition) {
  12.               throw new CustomException(resultCode);
  13.           }
  14.       }
  15.   }
  1. 异常捕获类
    使用@ControllerAdvice和@ExceptionHandler注解来捕获指定类型的异常
  1.   import com.google.common.collect.ImmutableMap;
  2.   import com.xuecheng.framework.model.response.CommonCode;
  3.   import com.xuecheng.framework.model.response.ResponseResult;
  4.   import com.xuecheng.framework.model.response.ResultCode;
  5.   import lombok.extern.slf4j.Slf4j;
  6.   import org.springframework.web.bind.annotation.ControllerAdvice;
  7.   import org.springframework.web.bind.annotation.ExceptionHandler;
  8.   import org.springframework.web.bind.annotation.ResponseBody;
  9.    
  10.   import java.net.SocketTimeoutException;
  11.    
  12.   /**
  13.    * @Author: jiangweifan
  14.    * @Date: 2019/3/4 20:13
  15.    * @Description:
  16.    */
  17.   @ControllerAdvice
  18.   @Slf4j
  19.   public class ExceptionCatch {
  20.    
  21.       @ExceptionHandler(CustomException.class)
  22.       @ResponseBody
  23.       public ResponseResult customException(CustomException e) {
  24.           log.error("catch exception : {} \r\nexception", e.getMessage(), e);
  25.           ResponseResult responseResult = new ResponseResult(e.getResultCode());
  26.           return responseResult;
  27.       }
  28.    
  29.   }

4.1 定义响应数据格式

  1.   import lombok.Data;
  2.   import lombok.NoArgsConstructor;
  3.   import lombok.ToString;
  4.    
  5.   /**
  6.    * @Author: mrt.
  7.    * @Description:
  8.    * @Date:Created in 2018/1/24 18:33.
  9.    * @Modified By:
  10.    */
  11.   @Data
  12.   @ToString
  13.   @NoArgsConstructor
  14.   public class ResponseResult implements Response {
  15.    
  16.       //操作是否成功
  17.       boolean success = SUCCESS;
  18.    
  19.       //操作代码
  20.       int code = SUCCESS_CODE;
  21.    
  22.       //提示信息
  23.       String message;
  24.    
  25.       public ResponseResult(ResultCode resultCode){
  26.           this.success = resultCode.success();
  27.           this.code = resultCode.code();
  28.           this.message = resultCode.message();
  29.       }
  30.    
  31.       public static ResponseResult SUCCESS(){
  32.           return new ResponseResult(CommonCode.SUCCESS);
  33.       }
  34.       public static ResponseResult FAIL(){
  35.           return new ResponseResult(CommonCode.FAIL);
  36.       }
  37.    
  38.   }
  39.    
  40.   其中Response代码如下
  41.   public interface Response {
  42.       public static final boolean SUCCESS = true;
  43.       public static final int SUCCESS_CODE = 10000;
  44.   }

4.2 定义错误代码(ResultCode上文已给出)

  1.   import com.xuecheng.framework.model.response.ResultCode;
  2.   import lombok.ToString;
  3.    
  4.   /**
  5.    * Created by mrt on 2018/3/5.
  6.    */
  7.   @ToString
  8.   public enum CmsCode implements ResultCode {
  9.       CMS_ADDPAGE_EXISTSNAME(false,24001,"页面名称已存在!"),
  10.       CMS_GENERATEHTML_DATAURLISNULL(false,24002,"从页面信息中找不到获取数据的url!"),
  11.       CMS_GENERATEHTML_DATAISNULL(false,24003,"根据页面的数据url获取不到数据!"),
  12.       CMS_GENERATEHTML_TEMPLATEISNULL(false,24004,"页面模板为空!"),
  13.       CMS_GENERATEHTML_HTMLISNULL(false,24005,"生成的静态html为空!"),
  14.       CMS_GENERATEHTML_SAVEHTMLERROR(false,24005,"保存静态html出错!"),
  15.       CMS_COURSE_PERVIEWISNULL(false,24007,"预览页面为空!"),
  16.       CMS_TEMPLATEFILE_ERROR(false,24008,"模板文件需要.ftl后缀!"),
  17.       CMS_TEMPLATEFILE_NULL(false,24009,"模板文件为空!"),
  18.       CMS_TEMPLATEFILE_EXCEPTION(false,24010,"解析模板文件异常!"),
  19.       CMS_TEMPLATEFILE_FAIL(false,24011,"模板文件存储失败!"),
  20.       CMS_TEMPLATEFILE_DELETE_ERROR(false,24012,"模板文件删除失败!"),
  21.       CMS_Config_NOTEXISTS(false,24013,"不存在该数据模型!"),
  22.       CMS_PAGE_NULL(false,24014,"不存在该页面数据!"),
  23.       CMS_GENERATEHTML_CONTENT_FAIL(false,24014,"获取页面模板失败!");
  24.       //操作代码
  25.       boolean success;
  26.       //操作代码
  27.       int code;
  28.       //提示信息
  29.       String message;
  30.       private CmsCode(boolean success, int code, String message){
  31.           this.success = success;
  32.           this.code = code;
  33.           this.message = message;
  34.       }
  35.    
  36.       @Override
  37.       public boolean success() {
  38.           return success;
  39.       }
  40.    
  41.       @Override
  42.       public int code() {
  43.           return code;
  44.       }
  45.    
  46.       @Override
  47.       public String message() {
  48.           return message;
  49.       }
  50.   }
  1. 在方法中抛出异常进行测试
  1.   @GetMapping("/list/{page}/{size}")
  2.   public QueryResponseResult findList(@PathVariable("page") int page, @PathVariable("size")int size, QueryPageRequest queryPageRequest) {
  3.       ExceptionCast.cast(CmsCode.CMS_COURSE_PERVIEWISNULL, queryPageRequest == null);
  4.       return pageService.findList(page,size,queryPageRequest);
  5.   }

最终方法得到以下结果
 

image

image

二、不可预知异常处理

  1. 在异常捕获类中添加对Exception类型异常的捕获,完整代码如下
  1.   import com.google.common.collect.ImmutableMap;
  2.   import com.xuecheng.framework.model.response.CommonCode;
  3.   import com.xuecheng.framework.model.response.ResponseResult;
  4.   import com.xuecheng.framework.model.response.ResultCode;
  5.   import lombok.extern.slf4j.Slf4j;
  6.   import org.springframework.web.bind.annotation.ControllerAdvice;
  7.   import org.springframework.web.bind.annotation.ExceptionHandler;
  8.   import org.springframework.web.bind.annotation.ResponseBody;
  9.    
  10.   import java.net.SocketTimeoutException;
  11.    
  12.   /**
  13.    * @Author: jiangweifan
  14.    * @Date: 2019/3/4 20:13
  15.    * @Description:
  16.    */
  17.   @ControllerAdvice
  18.   @Slf4j
  19.   public class ExceptionCatch {
  20.    
  21.       //使用EXCEPTIOS存放异常类型 和错误代码的映射,ImmutableMap的特点是已创建就不可变,并且线程安全
  22.       private static ImmutableMap<Class<? extends  Throwable>, ResultCode> EXCEPTIOS;
  23.       //是由builder来构建一个异常类型和错误代码的映射
  24.       private static ImmutableMap.Builder<Class<? extends  Throwable>, ResultCode> builder =
  25.               ImmutableMap.builder();
  26.    
  27.       static {
  28.           //初始化基础类型异常与错误代码的映射
  29.           builder.put(NullPointerException.class, CommonCode.NULL);
  30.           builder.put(SocketTimeoutException.class, CommonCode.NULL);
  31.       }
  32.    
  33.       @ExceptionHandler(CustomException.class)
  34.       @ResponseBody
  35.       public ResponseResult customException(CustomException e) {
  36.           log.error("catch exception : {} \r\nexception", e.getMessage(), e);
  37.           ResponseResult responseResult = new ResponseResult(e.getResultCode());
  38.           return responseResult;
  39.       }
  40.    
  41.       @ExceptionHandler(Exception.class)
  42.       @ResponseBody
  43.       public ResponseResult exception(Exception e) {
  44.           log.error("catch exception : {} \r\nexception", e.getMessage(), e);
  45.           if (EXCEPTIOS == null) {
  46.               EXCEPTIOS = builder.build();
  47.           }
  48.           final ResultCode resultCode = EXCEPTIOS.get(e.getClass());
  49.           if (resultCode != null) {
  50.               return new ResponseResult(resultCode);
  51.           } else {
  52.               return new ResponseResult(CommonCode.SERVER_ERROR);
  53.           }
  54.    
  55.       }
  56.    
  57.   }
  1.   import lombok.ToString;
  2.    
  3.   /**
  4.    * @Author: mrt.
  5.    * @Description:
  6.    * @Date:Created in 2018/1/24 18:33.
  7.    * @Modified By:
  8.    */
  9.    
  10.   @ToString
  11.   public enum CommonCode implements ResultCode{
  12.    
  13.       SUCCESS(true,10000,"操作成功!"),
  14.       FAIL(false,19999,"操作失败!"),
  15.       UNAUTHENTICATED(false,10001,"此操作需要登陆系统!"),
  16.       UNAUTHORISE(false,10002,"权限不足,无权操作!"),
  17.       NULL(false,10003,"空值异常!"),
  18.       TIMEOUT(false, 10004, "服务器连接超时!"),
  19.       SERVER_ERROR(false,99999,"抱歉,系统繁忙,请稍后重试!");
  20.   //    private static ImmutableMap<Integer, CommonCode> codes ;
  21.       //操作是否成功
  22.       boolean success;
  23.       //操作代码
  24.       int code;
  25.       //提示信息
  26.       String message;
  27.       private CommonCode(boolean success,int code, String message){
  28.           this.success = success;
  29.           this.code = code;
  30.           this.message = message;
  31.       }
  32.    
  33.       @Override
  34.       public boolean success() {
  35.           return success;
  36.       }
  37.       @Override
  38.       public int code() {
  39.           return code;
  40.       }
  41.    
  42.       @Override
  43.       public String message() {
  44.           return message;
  45.       }
  46.   }
  1. 方法中进行测试
  1.    @GetMapping("/list/{page}/{size}")
  2.   public QueryResponseResult findList(@PathVariable("page") int page, @PathVariable("size")int size, QueryPageRequest queryPageRequest) {
  3.       int a= 1/0;
  4.       return pageService.findList(page,size,queryPageRequest);
  5.   }

浏览器访问结果如下:
 

image

标签:ControllerAdvice,自定义,return,import,false,ResultCode,异常,public
来源: https://www.cnblogs.com/wuer888/p/14398008.html