SpringCloud微服务实战——搭建企业级开发框架(七)(自定义通用响应消息及统一异常处理)

??平时开发过程中,无可避免我们需要处理各类异常,所以这里我们在公共模块中自定义统一异常,Spring Boot 提供 @RestControllerAdvice 注解统一异常处理,我们在GitEgg_Platform中新建gitegg-platform-boot子工程,此工程主要用于Spring Boot相关功能的自定义及扩展。
1、修改gitegg-platform-boot的pom.xml,添加spring-boot-starter-web和swagger依赖,设置optional为true,让这个包在项目之间依赖不传递。

org.springframework.boot spring-boot-starter-web true com.gitegg.platform gitegg-platform-swagger true

2、自定义通用响应消息类,Result和PageResult,一个是普通响应消息,一个是分页响应消息。
Result类:
package com.gitegg.platform.boot.common.base; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.*; /** * @ClassName: Result * @Description: 自定义通用响应类 * @author GitEgg * @date 2020年09月19日 下午9:24:50 */ @ApiModel(description = "通用响应类") @Getter @ToString public class Result {@ApiModelProperty(value = "https://www.it610.com/article/是否成功", required = true) private boolean success; @ApiModelProperty(value ="https://www.it610.com/article/响应代码", required = true) private int code; @ApiModelProperty(value ="https://www.it610.com/article/提示信息", required = true) private String msg; @ApiModelProperty(value ="https://www.it610.com/article/响应数据") private T data; /** * * @param code * @param data * @param msg */ private Result(int code, T data, String msg) { this.success = ResultCodeEnum.SUCCESS.code == code; this.code = code; this.msg = msg; this.data = https://www.it610.com/article/data; }/** * * @param resultCodeEnum */ private Result(ResultCodeEnum resultCodeEnum ) { this(resultCodeEnum.code, null, resultCodeEnum.msg); }/** * * @param resultCodeEnum * @param msg */ private Result(ResultCodeEnum resultCodeEnum , String msg) { this(resultCodeEnum, null, msg); }/** * * @param resultCodeEnum * @param data */ private Result(ResultCodeEnum resultCodeEnum , T data) { this(resultCodeEnum, data, resultCodeEnum.msg); }/** * * @param resultCodeEnum * @param data * @param msg */ private Result(ResultCodeEnum resultCodeEnum , T data, String msg) { this(resultCodeEnum.code, data, msg); }/** * * * @param data 数据 * @param T 响应数据 * @ */ public static Result data(T data) { return data(data, ResultCodeEnum.SUCCESS.msg); }/** * * * @param data 数据 * @param msg消息 * @param T 响应数据 * @ */ public static Result data(T data, String msg) { return data(ResultCodeEnum.SUCCESS.code, data, msg); }/** * * * @param code 状态码 * @param data 数据 * @param msg消息 * @param T 响应数据 * @ */ public static Result data(int code, T data, String msg) { return new Result<>(code, data, msg); }/** * 返回Result * * @param * @param T 响应数据 * @返回Result */ public static Result success() { return new Result<>(ResultCodeEnum.SUCCESS); }/** * 返回Result * * @param msg 消息 * @param T 响应数据 * @返回Result */ public static Result success(String msg) { return new Result<>(ResultCodeEnum.SUCCESS, msg); }/** * 返回Result * * @param * @param T 响应数据 * @返回Result */ public static Result success(ResultCodeEnum resultCodeEnum ) { return new Result<>(resultCodeEnum); }/** * 返回Result * * @param * @param msg提示信息 * @param T 响应数据 * @返回Result */ public static Result success(ResultCodeEnum resultCodeEnum , String msg) { return new Result<>(resultCodeEnum, msg); }/** * 返回Result * * @param T 响应数据 * @返回Result */ public static Result error() { return new Result<>(ResultCodeEnum.ERROR, ResultCodeEnum.ERROR.msg); }/** * 返回Result * * @param msg 消息 * @param T 响应数据 * @返回Result */ public static Result error(String msg) { return new Result<>(ResultCodeEnum.ERROR, msg); }/** * 返回Result * * @param code 状态码 * @param msg消息 * @param T 响应数据 * @返回Result */ public static Result error(int code, String msg) { return new Result<>(code, null, msg); }/** * 返回Result * * @param * @param T 响应数据 * @返回Result */ public static Result error(ResultCodeEnum resultCodeEnum ) { return new Result<>(resultCodeEnum); }/** * 返回Result * * @param * @param msg提示信息 * @param T 响应数据 * @返回Result */ public static Result error(ResultCodeEnum resultCodeEnum , String msg) { return new Result<>(resultCodeEnum, msg); }/** * * @param * @param flag * @return */ public static Result result(boolean flag) { return flag ? Result.success("操作成功") : Result.error("操作失败"); }}

PageResult类:
package com.gitegg.platform.boot.common.base; import java.util.List; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * @ClassName: PageResult * @Description: 通用分页返回 * @author GitEgg * @date * @param */ @Data @ApiModel("通用分页响应类") public class PageResult {@ApiModelProperty(value = "https://www.it610.com/article/是否成功", required = true) private boolean success; @ApiModelProperty(value ="https://www.it610.com/article/响应代码", required = true) private int code; @ApiModelProperty(value ="https://www.it610.com/article/提示信息", required = true) private String msg; @ApiModelProperty(value ="https://www.it610.com/article/总数量", required = true) private long count; @ApiModelProperty(value ="https://www.it610.com/article/分页数据") private List data; public PageResult(long total, List rows) { this.count = total; this.data = https://www.it610.com/article/rows; this.code = ResultCodeEnum.SUCCESS.code; this.msg = ResultCodeEnum.SUCCESS.msg; } }

3、自定义通用响应消息枚举类ResultCodeEnum。
package com.gitegg.platform.boot.common.enums; /** * @ClassName: ResultCodeEnum * @Description: 自定义返回码枚举 * @author GitEgg * @date 2020年09月19日 下午11:49:45 */ public enum ResultCodeEnum {/** * 成功 */ SUCCESS(200, "操作成功"),/** * 系统错误 */ ERROR(500, "系统错误"),/** * 操作失败 */ FAILED(101, "操作失败"),/** * 未登录/登录超时 */ UNAUTHORIZED(102, "登录超时"),/** * 参数错误 */ PARAM_ERROR(103, "参数错误"),/** * 参数错误-已存在 */ INVALID_PARAM_EXIST(104, "请求参数已存在"),/** * 参数错误 */ INVALID_PARAM_EMPTY(105, "请求参数为空"),/** * 参数错误 */ PARAM_TYPE_MISMATCH(106, "参数类型不匹配"),/** * 参数错误 */ PARAM_VALID_ERROR(107, "参数校验失败"),/** * 参数错误 */ ILLEGAL_REQUEST(108, "非法请求"),/** * 验证码错误 */ INVALID_VCODE(204, "验证码错误"),/** * 用户名或密码错误 */ INVALID_USERNAME_PASSWORD(205, "账号或密码错误"),/** * */ INVALID_RE_PASSWORD(206, "两次输入密码不一致"),/** * 用户名或密码错误 */ INVALID_OLD_PASSWORD(207, "旧密码错误"),/** * 用户名重复 */ USERNAME_ALREADY_IN(208, "用户名已存在"),/** * 用户不存在 */ INVALID_USERNAME(209, "用户名不存在"),/** * 角色不存在 */ INVALID_ROLE(210, "角色不存在"),/** * 角色不存在 */ ROLE_USED(211, "角色使用中,不可删除"),/** * 没有权限 */ NO_PERMISSION(403, "当前用户无该接口权限"); public int code; public String msg; ResultCodeEnum(int code, String msg) { this.code = code; this.msg = msg; }public int getCode() { return code; }public void setCode(int code) { this.code = code; }public String getMsg() { return msg; }public void setMsg(String msg) { this.msg = msg; } }

4、自定义异常类BusinessException和SystemException
package com.gitegg.platform.boot.common.exception; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.Setter; /** * @ClassName: BusinessException * @Description: 业务处理异常 * @author GitEgg * @date */ @Getter @Setter public class BusinessException extends RuntimeException {private int code; private String msg; public BusinessException() { this.code = ResultCodeEnum.FAILED.code; this.msg = ResultCodeEnum.FAILED.msg; }public BusinessException(String message) { this.code = ResultCodeEnum.FAILED.code; this.msg = message; }public BusinessException(int code, String msg) { this.code = code; this.msg = msg; }public BusinessException(Throwable cause) { super(cause); }public BusinessException(String message, Throwable cause) { super(message, cause); }}

package com.gitegg.platform.boot.common.exception; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import lombok.Getter; /** * @ClassName: SystemException * @Description: 系统处理异常 * @author GitEgg * @date */ @Getter public class SystemException extends RuntimeException {private int code; private String msg; public SystemException() { this.code = ResultCodeEnum.ERROR.code; this.msg = ResultCodeEnum.ERROR.msg; }public SystemException(String message) { this.code = ResultCodeEnum.ERROR.code; this.msg = message; }public SystemException(int code, String msg) { this.code = code; this.msg = msg; }public SystemException(Throwable cause) { super(cause); }public SystemException(String message, Throwable cause) { super(message, cause); }}

5、自定义统一异常处理类GitEggControllerAdvice.java
package com.gitegg.platform.boot.common.advice; import com.gitegg.platform.boot.common.base.Result; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import com.gitegg.platform.boot.common.exception.BusinessException; import com.gitegg.platform.boot.common.exception.SystemException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.ui.Model; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingPathVariableException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolationException; @Slf4j @RestControllerAdvice public class GitEggControllerAdvice {/** * 服务名 */ @Value("${spring.application.name}") private String serverName; /** * 微服务系统标识 */ private String errorSystem; @PostConstruct public void init() { this.errorSystem = new StringBuffer() .append(this.serverName) .append(": ").toString(); }/** * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器 */ @InitBinder public void initBinder(WebDataBinder binder) {}/** * 把值绑定到Model中,使全局@RequestMapping可以获取到该值 */ @ModelAttribute public void addAttributes(Model model) {}/** * 全局异常捕捉处理 */ @ExceptionHandler(value = https://www.it610.com/article/{Exception.class}) public Result handlerException(Exception exception, HttpServletRequest request) { log.error("请求路径uri={},系统内部出现异常:{}", request.getRequestURI(), exception); Result result = Result.error(ResultCodeEnum.ERROR, errorSystem + exception.toString()); return result; }/** * 非法请求异常 */ @ExceptionHandler(value = https://www.it610.com/article/{ HttpMediaTypeNotAcceptableException.class, HttpMediaTypeNotSupportedException.class, HttpRequestMethodNotSupportedException.class, MissingServletRequestParameterException.class, NoHandlerFoundException.class, MissingPathVariableException.class, HttpMessageNotReadableException.class }) public Result handlerSpringAOPException(Exception exception) { Result result = Result.error(ResultCodeEnum.ILLEGAL_REQUEST, errorSystem + exception.getMessage()); return result; }/** * 非法请求异常-参数类型不匹配 */ @ExceptionHandler(value = MethodArgumentTypeMismatchException.class) public Result handlerSpringAOPException(MethodArgumentTypeMismatchException exception) { Result result = Result.error(ResultCodeEnum.PARAM_TYPE_MISMATCH, errorSystem + exception.getMessage()); return result; }/** * 非法请求-参数校验 */ @ExceptionHandler(value = {MethodArgumentNotValidException.class}) public Result handlerMethodArgumentNotValidException(MethodArgumentNotValidException methodArgumentNotValidException) { //获取异常字段及对应的异常信息 StringBuffer stringBuffer = new StringBuffer(); methodArgumentNotValidException.getBindingResult().getFieldErrors().stream() .map(t -> t.getField()+"=>"+t.getDefaultMessage()+" ") .forEach(e -> stringBuffer.append(e)); String errorMessage = stringBuffer.toString(); Result result = Result.error(ResultCodeEnum.PARAM_VALID_ERROR, errorSystem + errorMessage); return result; }/** * 非法请求异常-参数校验 */ @ExceptionHandler(value = https://www.it610.com/article/{ConstraintViolationException.class}) public Result handlerConstraintViolationException(ConstraintViolationException constraintViolationException) { String errorMessage = constraintViolationException.getLocalizedMessage(); Result result = Result.error(ResultCodeEnum.PARAM_VALID_ERROR, errorSystem + errorMessage); return result; }/** * 自定义业务异常-BusinessException */ @ExceptionHandler(value = {BusinessException.class}) public Result handlerCustomException(BusinessException exception) { String errorMessage = exception.getMsg(); Result result = Result.error(exception.getCode(), errorSystem + errorMessage); return result; }/** * 自定义系统异常-SystemException */ @ExceptionHandler(value = {SystemException.class}) public Result handlerCustomException(SystemException exception) { String errorMessage = exception.getMsg(); Result result = Result.error(exception.getCode(), errorSystem + errorMessage); return result; }}

6、重新将GitEgg-Platform进行install,在GitEgg-Cloud中的gitegg-service引入gitegg-platform-boot
【SpringCloud微服务实战——搭建企业级开发框架(七)(自定义通用响应消息及统一异常处理)】GitEgg-Cloud com.gitegg.cloud 1.0-SNAPSHOT 4.0.0gitegg-servicepom gitegg-service-base gitegg-service-bigdata gitegg-service-system com.gitegg.platform gitegg-platform-boot com.gitegg.platform gitegg-platform-db com.gitegg.platform gitegg-platform-mybatis com.gitegg.platform gitegg-platform-swagger org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator

7、修改SystemController.java、ISystemService.java和SystemServiceImpl.java增加异常处理的测试代码
SystemController.java:
package com.gitegg.service.system.controller; import com.gitegg.platform.boot.common.base.Result; import com.gitegg.platform.boot.common.exception.BusinessException; import com.gitegg.service.system.service.ISystemService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(value = "https://www.it610.com/article/system") @AllArgsConstructor @Api(tags = "gitegg-system") public class SystemController {private final ISystemService systemService; @GetMapping(value = "https://www.it610.com/article/list") @ApiOperation(value = "https://www.it610.com/article/system list接口") public Object list() { return systemService.list(); }@GetMapping(value = "https://www.it610.com/article/page") @ApiOperation(value = "https://www.it610.com/article/system page接口") public Object page() { return systemService.page(); }@GetMapping(value = "https://www.it610.com/article/exception") @ApiOperation(value = "https://www.it610.com/article/自定义异常及返回测试接口") public Result exception() { return Result.data(systemService.exception()); } }

ISystemService.java:
package com.gitegg.service.system.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gitegg.service.system.entity.SystemTable; import java.util.List; public interface ISystemService {List list(); Page page(); String exception(); }

SystemServiceImpl.java:
package com.gitegg.service.system.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gitegg.platform.boot.common.exception.BusinessException; import com.gitegg.service.system.entity.SystemTable; import com.gitegg.service.system.mapper.SystemTableMapper; import com.gitegg.service.system.service.ISystemService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; /** * */ @Service @AllArgsConstructor public class SystemServiceImpl implements ISystemService {private final SystemTableMapper systemTableMapper; @Override public List list() { return systemTableMapper.list(); }@Override public Page page() { Page page = new Page<>(1, 10); List records = systemTableMapper.page(page); page.setRecords(records); return page; }@Override public String exception() { throw new BusinessException("自定义异常"); //return "成功获得数据"; } }

8、运行GitEggSystemApplication,打开浏览器访问:http://127.0.0.1:8001/doc.html,然后点击左侧的异常处理接口,使用Swagger2进行测试,即可看到结果
SpringCloud微服务实战——搭建企业级开发框架(七)(自定义通用响应消息及统一异常处理)
文章图片

本文源码在https://gitee.com/wmz1930/GitEgg 的chapter-07分支。

    推荐阅读