spring Boot手把手教学(7): 抛弃try-catch, 如何优雅统一处理异常(包含404处理)

1、前言

我们在项目开发中,难免碰到业务代码异常,无论是server 500, 还是其他异常。 我们这里简单说一下,如何抛弃try-catch,统一进行异常响应处理;

错误类型:

1、自定义业务服务代码异常(根据各自项目需求)

2、ServletException HTTP请求异常

3、内部代码异常:比如 mysql 查询表名错误

4、请求接口404:这个在统一异常中无法获取,需要额外处理

正常情况下,我们需要这么写:

JobController

 // 查询所有
    @GetMapping("/list")
    public Result getList() {
        Result result;
        try {
            List<Job> jobList = jobService.findAll();
            result = ResultGenerator.getSuccessResult(jobList);
        } catch (Exception e) {
            e.printStackTrace();
            result = ResultGenerator.getFailResult(e.getMessage());
        }
        return result;
    }

结果如图:

这样比较麻烦,要写很多try-catch.

那么有没有一种办法,帮我们自动拦截错误呢?

2、自定义异常处理

HandlerExceptionResolver, 就是处理异常的类;

源码:

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}

以上有四个参数其实相当于:request(请求)、response(响应)、hendler(处理器)、exception(异常);

接下来我们重写 WebMvcConfigurer.configureHandlerExceptionResolvers来处理异常:

com.scaffold.test.config.WebMvcConfig

package com.scaffold.test.config;

import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List;  @Configuration public class WebMvcConfig implements WebMvcConfigurer {   private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);   // 统一异常处理  @Override  public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {  exceptionResolvers.add((request, response, handler, e) -> {  Result result = new Result();  // 错误处理  if (e instanceof ServiceException) {  // 1、业务失败的异常,如“账号或密码错误”  result.setCode(ResultCode.FAIL).setMessage(e.getMessage());  logger.info(e.getMessage());  } else if (e instanceof ServletException) {  // 2、调用失败  result.setCode(ResultCode.FAIL).setMessage(e.getMessage());  } else {  // 3、内部错误  result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");  String message;  if (handler instanceof HandlerMethod) {  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);  return new ModelAndView();  });  }   // 处理返回数据格式  private void responseResult(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());  }  }  } 

1、业务失败的异常: ServiceException 服务异常类

package com.scaffold.test.base;

/**
 * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer
 */
public class ServiceException extends RuntimeException {  public ServiceException() {  }   public ServiceException(String message) {  super(message);  }   public ServiceException(String message, Throwable cause) {  super(message, cause);  } }  
// 部分代码
if (e instanceof ServiceException) {
  // 1、业务失败的异常,如“账号或密码错误”
  result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
  logger.info(e.getMessage());
} 

报错400代码;

2、调用失败: Servlet异常

if (e instanceof ServletException) {
 // 3、调用失败
 result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
}

​ 结果如图:

3、内部代码异常:比如 mysql 查询表名错误

String message;
if (handler instanceof HandlerMethod) {
 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); 

结果如图:

4、请求接口404:这个在统一异常中无法获取,需要额外处理

404进入不了统一异常处理,如下图:

所以,全局异常处理捕获不到404错误,需要专门写一个类用来处理404异常

com.scaffold.test.base.NotFoundException

package com.scaffold.test.base;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 @RestController public class NotFoundException implements ErrorController {   private static final String ERROR_PATH = "/error";   @RequestMapping(ERROR_PATH)  public Result error() {  Result result = new Result();  // 2、接口不存在  result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在");  return result;  }   @Override  public String getErrorPath() {  return ERROR_PATH;  } } 

3、完整代码

com.scaffold.test.base.Result: 统一返回实体类

package com.scaffold.test.base;

import lombok.Data;

/**  * 统一API响应结果封装  */  @Data public class Result {   private int code;   private String message = "success";   private Object data;   public Result setCode(ResultCode resultCode){  this.code = resultCode.code;  return this;  }   public Result setMessage(String message){  this.message = message;  return this;  }   public Result setData(Object data){  this.data = data;  return this;  }  } 

com.scaffold.test.base.ResultCode: 统一返回响应码枚举,参考HTTP状态码

package com.scaffold.test.base;

/**
 * 响应码枚举,参考HTTP状态码的语义
 */
 public enum ResultCode {  SUCCESS(200),//成功  FAIL(400),//失败  UNAUTHORIZED(401),//未认证(签名错误)  NOT_FOUND(404),//接口不存在  INTERNAL_SERVER_ERROR(500);//服务器内部错误   public int code;   ResultCode(int code) {  this.code = code;  } } 

统一异常拦截:com.scaffold.test.config.WebMvcConfig

package com.scaffold.test.config;

import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List;  @Configuration public class WebMvcConfig implements WebMvcConfigurer {   private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);   // 统一异常处理  @Override  public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {  exceptionResolvers.add((request, response, handler, e) -> {  Result result = new Result();  // 异常处理  if (e instanceof ServiceException) {  // 1、业务失败的异常,如“账号或密码错误”  result.setCode(ResultCode.FAIL).setMessage(e.getMessage());  logger.info(e.getMessage());  }else if (e instanceof ServletException) {  // 2、调用失败  result.setCode(ResultCode.FAIL).setMessage(e.getMessage());  } else {  // 3、内部其他错误  result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");  String message;  if (handler instanceof HandlerMethod) {  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);  return new ModelAndView();  });  }   // 处理响应数据格式  private void responseResult(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());  }  } } 

404拦截处理:com.scaffold.test.base.NotFoundException

package com.scaffold.test.base;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 @RestController public class NotFoundException implements ErrorController {   private static final String ERROR_PATH = "/error";   @RequestMapping(ERROR_PATH)  public Result error() {  Result result = new Result();  // 4、接口不存在  result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在");  return result;  }   @Override  public String getErrorPath() {  return ERROR_PATH;  } }  

本文使用 mdnice 排版

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值