基于springboot的全局异常、自定义异常、统一返回值处理

正经学徒,佛系记录,不搞事情

为什么将三个问题结合起来记录,因为这三个问题往往都是息息相关的,虽说看起来不那么有技术含量,但却是一个项目规范的重要起步,现在通过一个springboot项目来解释一下这三点

统一返回值

springMVC项目只要加上一个@ResponseBody注解就可以返回任意的数据类型,但也就是因为没有对这一层进行把控,导致不同的人写的代码返回值千奇百怪,有的返回集合、有的返回字符串、有的返回数字甚至是返回布尔。尤其是在没有代码审查的项目中,一旦有新人加入,那将会为以后维护的人埋下一个又一个的坑。

统一返回值的优点:

  1. 规范后台返回的数据格式
  2. 前端可以统一根据返回的结果触发相应的消息通知(尤其是在前后端分离的项目)

接下来创建一个普通的springboot项目,添加web依赖

这是我的自定义统一返回对象 ResultJson.Java

public class ResultJson {
	/**
     * @escription 成功返回true,失败返回false
     */
    private boolean success;
    /**
     * @返回的附带信息,用于客户端提示给用户
     */
    private String message;
    /**
     * @返回的数据
     */
    private Map<String, Object> data;

    public ResultJson(){}

    public ResultJson(Map<String, Object> data, Boolean success, String message){
    	this.success=success;
    	this.message=message;
    	if(data != null){
    		this.data=data;
    	}else{
    		this.data=null;
    	}
    }
    
    /**
     * 返回成功的请求
     * @param data 成功的数据
     * @param message 成功的提示
     * @return
     */
    public static ResultJson success(Map<String, Object> data,String message) {
        return new ResultJson(data,true, message);
    }
    
    /**
     * 返回成功的请求
     * @param data 成功的数据
     * @return
     */
    public static ResultJson success(Map<String, Object> data) {
        return new ResultJson(data,true, "请求成功!");
    }
    
    /**
     * 返回成功的请求
     * @param message 成功的提示
     * @return
     */
    public static ResultJson success(String message) {
        return new ResultJson(null,true, message);
    }
    
    /**
     * 返回失败的请求
     * @param message 失败的提示
     * @return
     */
    public static ResultJson fail(String message) {
        return new ResultJson(null,false, message);
    }
    
	public boolean isSuccess() {
		return success;
	}
	public void setSuccess(boolean success) {
		this.success = success;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	public Map<String, Object> getData() {
		return data;
	}
	public void setData(Map<String, Object> data) {
		this.data = data;
	}
}
  • success:判断请求是否成功,值为true、false
  • message:通知的消息,前端可直接输出到页面提示给用户
  • data:返回值,之所以使用map,是为了适应不同的返回类型
  • 各种静态方法:一些默认值得控制

使用统一返回对象,创建一个TestController.java

@RestController
public class TestController {
    @RequestMapping("/test")
    public ResultJson test (){
        Map<String,Object> retMap = new HashMap<>();
        retMap.put("data1","来自四方的数据1");
        retMap.put("data2",new String[]{"来自四方的数据2","来自四方的数据3"});
        return ResultJson.success(retMap);
    }
}

@RestController 等价于 @Controller + @ResponseBody

全局异常处理

首先来试试看,如果没有使用全局异常处理返回值将会是怎么样的

修改TestController方法

@RequestMapping("/test")
public ResultJson test (){
    int i=1/0;
    Map<String,Object> retMap = new HashMap<>();
    retMap.put("data1","来自四方的数据1");
    retMap.put("data2",new String[]{"来自四方的数据2","来自四方的数据3"});
    return ResultJson.success(retMap);
}

 由于添加了 1/0 的方法,这里将会抛出异常,而异常又区分为,请求数据异常访问页面异常。请求数据时,一般使用ajax,而其余请求我们则返回404页面。

请求数据异常:

访问页面异常:

返回的数据格式和页面都不是我们所需要的,我们也不希望用户看到这样的数据和页面,也正是因为返回的数据格式和页面不是我们所需要的,所以才要结合全局异常处理统一返回值,springboot通过@ControllerAdvice+@ExceptionHandler来捕捉异常

添加 GlobalExceptionHandler.java

/**
 * @description 全局异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    public Object errorHandler(HttpServletRequest request,
                               HttpServletResponse response, Exception e) throws  Exception{
        e.printStackTrace();
        if(isAjax(request)){
            try {
                response.setContentType("application/json; charset=UTF-8");
                response.getWriter().write(JSON.toJSONString(ResultJson.fail("操作失败,原因:“"+e.getMessage()+"”,请联系管理员处理")));
                response.getWriter().close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return null;
        }else{
            //返回错误页面
            ModelAndView mav = new ModelAndView();
            mav.addObject("exception",e);
            mav.addObject("url",request.getRequestURL());
            mav.setViewName("404");
            return mav;
        }
    }

    /**
     * @description 判断是否是ajax请求
     * @return boolean
     */
    public static boolean isAjax(HttpServletRequest httpRequest){
        return (httpRequest.getHeader("X-Requested-With")!=null
                && "XMLHttpRequest"
                    .equals(httpRequest.getHeader("X-Requested-With").toString()));
    }
}

 这里通过isAjax方法判断当前的请求是请求数据异常还是访问页面异常,由于需要请求数据异常要返回我们自定义的对象,这里使用到了alibaba  fastjson,访问页面异常使用到了页面(springboot推荐使用thymeleaf模板引擎)所以在pom.xml中引入

<!--fastjson数据解析器-->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.33</version>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置文件application.properties添加:

# 定位模板的目录
spring.mvc.view.prefix=classpath:/templates/
# 给返回的页面添加后缀名
spring.mvc.view.suffix=.html

templates下添加一个404.html页面

在进行测试,访问页面异常:

请求数据异常,给postman添加request模拟ajax请求:

注:/ by zero即是分母为0报出的错误

自定义异常

最后就是这个自定义异常,有时候我们需要自己抛出异常,比如某某参数值为空,某某查询结果为空

添加BaseException.java

public class BaseException extends RuntimeException {
    public BaseException() {
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }

    public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

继承运行时异常,修改GlobalExceptionHandler.java,追加baseErrorHandler方法来处理自定义异常

@ExceptionHandler(value = BaseException.class)
public Object baseErrorHandler(HttpServletRequest request,
                           HttpServletResponse response, Exception e) throws  Exception{
    e.printStackTrace();
    //抛出自定义异常
    try {
        response.setContentType("application/json; charset=UTF-8");
        response.getWriter().write(JSON.toJSONString(ResultJson.fail(e.getMessage())));
        response.getWriter().close();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

测试:主动throw异常

@RequestMapping("/test")
public ResultJson test (){
    if(true){
        throw new BaseException("用户名不能为空");
    }
    int i=1/0;
    Map<String,Object> retMap = new HashMap<>();
    retMap.put("data1","来自四方的数据1");
    retMap.put("data2",new String[]{"来自四方的数据2","来自四方的数据3"});
    return ResultJson.success(retMap);
}

结果:

项目地址: https://pan.baidu.com/s/1E_PbVzGGptv218AlmI40bw 提取码: m6em

猜你喜欢

转载自blog.csdn.net/qq_31748587/article/details/86649255