springboot-web进阶(三)——统一异常处理

补充

  springboot中也是一样的可以对结果进行统一格式的包装,这样也就方便了前台的统一接收处理了;

  1.结果集包装类 

package com.example.demo.bean;

/**
* 结果包装
*
* @author zcc ON 2018/2/9
**/
public class Result<T> {
/**
* 错误代码(可以设定例如500表示错误)
*/
private Integer code;
/**
* 提示信息
*/
private String msg;
/**
* 数据内容
*/
private T data; public Result() {
} public Integer getCode() {
return code;
} public void setCode(Integer code) {
this.code = code;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
} public T getData() {
return data;
} public void setData(T data) {
this.data = data;
}
}

Resultde

    可以参考之前SSM中的包装类http://www.cnblogs.com/jiangbei/p/7080955.html

  2.使用包装结果集

@PostMapping(value = "/girls")
public Result<Girl> addGirl(@Valid Girl girl, BindingResult bindingResult) {
Result<Girl> result = new Result<>();
// 表单验证逻辑
if (bindingResult.hasErrors()) {
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
StringBuffer sb = new StringBuffer();
for (FieldError fieldError : fieldErrors) {
sb.append(fieldError.getDefaultMessage());
}
result.setCode(500);
result.setMsg(sb.toString());
return result;
}
result.setCode(200);
result.setMsg("success");
result.setData(girlRepository.save(girl));
return result;
}

    3。改进结果集

      通过代码观察或者对比SSM中的示例,都发现这样写在代码中是挺傻的,这样,我们提供一个工具类(或者在结果集中直接封装)

/**
* 结果集的工具类
*
* @author zcc ON 2018/2/9
**/
public class ResultUtils { public static Result<Object> success(Object data) {
Result<Object> result = new Result<>();
result.setCode(200);
result.setMsg("success");
result.setData(data); return result;
} public static Result<Object> error(String msg) {
Result<Object> result = new Result<>();
result.setCode(500);
result.setMsg(msg);
return result;
}
}

    使用:

 @PostMapping(value = "/girls")
public Result<Object> addGirl(@Valid Girl girl, BindingResult bindingResult) {
// 表单验证逻辑
if (bindingResult.hasErrors()) {
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
StringBuffer sb = new StringBuffer();
for (FieldError fieldError : fieldErrors) {
sb.append(fieldError.getDefaultMessage());
}
return ResultUtils.error(sb.toString());
}
return ResultUtils.success(girlRepository.save(girl));
}

一、概述

  springboot提供了默认的统一异常处理,basicErrorController,这个controller提供了默认了错误处理方法,包括错误跳转的路径和渲染方法。

  因为默认的错误处理方法可能会不适合项目的需要,所以当需要自定义统一错误处理的时候,可以继承basicErrorController。或重新实现basicErrorController.

统一异常处理可配置400,404,500等状态异常错误处理。basicErrorController内部提供了针对accept=**的不同处理方法,可区别是普通调用还是ajax等其他调用。

  缺点:需要重写basicErrorController。并配置覆盖原来springboot的默认配置。当使用EmbeddedServletContainerCustomizer类配置400,404。500的时候,暂时不清楚调用springboot的处理方法。

二、问题提出

  假设需要根据girl的不同age作出不同的动作,controller如下:

 @GetMapping(value = "/girls/getAge/{id}")
public void getAge(@PathVariable("id") Integer id) {
// 判断应该交给service

  service:

public void getAge(Integer id) {
Integer age = girlRepository.findOne(id).getAge();
if (age < 12) {
// 小学生
} else {
// 上初中了
}
}

  如果只是简单的判断,不同年龄返回不同信息给前台,那确实可以通过返回一个String来进行

  但是,如果业务比较复杂,需要的不仅仅是返回一个字符串,而是需要其他动作,这个时候就显得有点力不从心了!

三、统一异常处理

   1.基本用法

   controller调用service,抛出异常即可:

 @GetMapping(value = "/girls/getAge/{id}")
public void getAge(@PathVariable("id") Integer id) throws Exception{
// 判断应该交给service
girlService.getAge(id);
}

  service也是,该抛异常就抛出异常:

public void getAge(Integer id) throws Exception{
Integer age = girlRepository.findOne(id).getAge();
if (age < 12) {
// 小学生
throw new Exception("小学生!");
} else {
// 上初中了
throw new Exception("初中生!");
}
}

  统一异常处理类:

package com.example.demo.handle;

import com.example.demo.bean.Result;
import com.example.demo.utils.ResultUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; /**
* 异常处理类
* 添加@ControllerAdvice标记为异常处理类
* @author zcc ON 2018/2/9
**/
@ControllerAdvice
public class MyExceptionHandler { /**
* 如果不在这里加@ResponseBody,则需要在类上加
* @param e 异常
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result handle(Exception e) {
return ResultUtils.error(e.getMessage());
}
}

  2.自定义异常

    写自定义异常还是之前的基础篇的老套路,基本就是几个构造方法的编写,再根据需要加上自己的变量!

package com.example.demo.exception;

/**
* 自定义异常
* 继承RuntimeException才能使得spring进行异常时的回滚
* @author zcc ON 2018/2/9
**/
public class MyException extends RuntimeException{
private Integer code; public MyException(Integer code, String msg) {
super(msg);
this.code = code;
} public Integer getCode() {
return code;
} public void setCode(Integer code) {
this.code = code;
}
}

    异常处理类:

package com.example.demo.handle;

import com.example.demo.bean.Result;
import com.example.demo.exception.MyException;
import com.example.demo.utils.ResultUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; /**
* 异常处理类
* 添加@ControllerAdvice标记为异常处理类
* @author zcc ON 2018/2/9
**/
@ControllerAdvice
public class MyExceptionHandler { /**
* 如果不在这里加@ResponseBody,则需要在类上加
* @param e 异常
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result handle(Exception e) {
// 先判断捕获的是不是我们定义的异常
if (e instanceof MyException) {
MyException me = (MyException) e;
return ResultUtils.exp(me.getCode(), me.getMessage());
} else {
return ResultUtils.error("未知错误!");
}
}
}

  其他地方需要注意:

    既然MyException继承RuntimeException了,那controller和service就不用抛出异常了

    ResultUtils根据需要微调一些

  统一404、500异常处理

  @ExceptionHandler(value = Exception.class)
@ResponseBody
public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
logger.error("", e);
ResponseData r = new ResponseData();
r.setMessage(e.getMessage());
if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
r.setCode(404);
} else {
r.setCode(500);
}

三、使用枚举优化管理

  1.枚举

package com.example.demo.enums;

/**
* 结果集的枚举管理类
* 不用给出setter了,因为枚举不会再去set了
* @author zcc
**/
public enum ResultEnum {
UNKNOWN(-1, "未知错误"),
SUCCESS(200, "success"),
PRIMARY(100, "小学生"),
MIDDLE(101, "中学生"); private Integer code;
private String msg; ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
} public Integer getCode() {
return code;
} public String getMsg() {
return msg;
}
}

  更多枚举基础知识,参考:http://www.cnblogs.com/jiangbei/p/7580482.html

  2.调整自定义异常的构造方法

/**
* 自定义异常
* 继承RuntimeException才能使得spring进行异常时的回滚
* @author zcc ON 2018/2/9
**/
public class MyException extends RuntimeException{
private Integer code; public MyException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
} public Integer getCode() {
return code;
} public void setCode(Integer code) {
this.code = code;
}
}

  3.使用自定义枚举统一管理错误代码和错误信息之间的关系

 public void getAge(Integer id) {
Integer age = girlRepository.findOne(id).getAge();
if (age < 12) {
// 小学生
throw new MyException(ResultEnum.PRIMARY);
} else {
// 上初中了
throw new MyException(ResultEnum.MIDDLE);
}
}
上一篇:VS2017连接Oracle设置


下一篇:GET和POST两种基本请求方法的区别(转载)