构建spring boot web项目:三、设置统一返回、统一异常处理

目录

一、统一结果返回

二、统一异常处理

三、测试

一、统一结果返回

在基础模块设置(base)

接口统一返回类R.java
package com.lyj.service.common;


import com.lyj.service.enums.ErrorCodeEnum;
import lombok.Getter;

import java.io.Serializable;

/**
 *接口统一返回值
 */
@Getter
public class R<T> implements Serializable {

    private static final long serialVersionUID = 7498171848815486866L;

    /**
     * 系统定义的业务错误码(非HTTP标准状态码)
     *
     * @seecom.lyj.service.enums.ErrorCodeEnum
     */
    private int code;

    /**
     * 系统定义的业务错误信息
     *
     * @see com.lyj.service.enums.ErrorCodeEnum
     */
    private String msg;

    /**
     * 本次请求的返回值
     */
    private T data;
    /**
     * 时间戳
     */
    private long timestamp ;

    public R(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
        this.timestamp = System.currentTimeMillis();
    }

    public R() {
    }

    private R(int code, String msg) {
        this(code, msg, null);
    }

    /**
     * 封装请求成功的返回值
     *
     * @param data 返回值
     * @return R
     */
    public static <T> R<T> ok(T data) {
        return new R<>(ErrorCodeEnum.SUCCESS.getCode(), ErrorCodeEnum.SUCCESS.getMessage(), data);
    }

    /**
     * 封装请求失败的返回值
     *
     * @param errorCode 系统定义的业务错误码和错误信息
     * @return R
     */
    public static <T> R<T> error(ErrorCodeEnum errorCode) {
        return new R<>(errorCode.getCode(), errorCode.getMessage());
    }

    /**
     * 封装请求失败的返回值
     *
     * @param code    自定义业务错误码
     * @param message 自定义业务错误信息
     * @return R
     */
    public static <T> R<T> error(int code, String message) {
        return new R<>(code, message);
    }

    /**
     * 封装请求失败的返回值(系统定义的业务错误码+自定义业务错误信息)
     *
     * @param message 自定义业务错误信息
     * @return R
     */
    public static <T> R<T> error(String message) {
        return new R<>(ErrorCodeEnum.BUSINESS_EXCEPTION.getCode(), message);
    }

}
异常枚举类ErrorCodeEnum.java
package com.lyj.common.base.enums;

import lombok.Getter;

/**
 * 异常枚举类
 */
@Getter
public enum ErrorCodeEnum {
    // TODO 错误码需要进行规划调整

    // 成功
    SUCCESS(0, "成功"),

    // 40xxx表示请求方原因导致的异常

    /**
     * 通用异常(400xx)
     */
    BUSINESS_EXCEPTION(40000, "业务异常"),
    PARAMETER_ERROR(40001, "参数异常"),

    /**
     * 参数异常(401xx)
     */
    MISSING_PARAMETERS(40100, "参数缺失"),

    /**
     * 用户异常(4021X)
     */
    USER_NOT_EXIST(40210, "用户不存在"),
    USER_PHONE_NOT_EXIST(40210, "用户手机不存在"),
    USER_NAME_DUPLICATE(40211, "用户名称重复"),
    USER_DUPLICATE(40212, "用户重复添加"),
    USER_ALREADY_BIND(40213, "用户已经绑定微信,重新绑定需退出原绑定微信"),
    USER_NO_WORKGROUP(40214, "用户部门没有关联工作组"),
    USER_NO_DEPT(40215, "用户没有部门信息"),
    USER_DELETE_SELF_ERROR(40216, "您无法删除自己"),
    USER_NOT_PHONE(40217, "未获取到有效手机号码,请联系智慧云门户配置"),
    USER_NAME_EMPTY(40009, "用户名不能为空"),
    USER_PASSWORD_EMPTY(40010, "密码不能为空"),

    /**
     * 角色异常(4022X)
     */
    ROLE_NOT_EXIST(40220, "角色不存在"),
    ROLE_NAME_DUPLICATE(40221, "角色名称重复"),
    USER_ROLE_DUPLICATE(40222, "用户已关联此角色"),
    USER_NO_ROLE(40223, "用户未分配角色,请联系管理员处理"),
    REL_ROLE_ERROR(40224, "关联角色失败"),
    ROLE_IN_USE(40225, "角色使用中"),
    USE_DELETE_ERROR(40226, "无法删除"),

    /**
     * 部门异常(4023X)
     */

    /**
     * 认证异常(403xx)
     */
    TOKEN_GENERATE_ERROR(40300, "生成token错误"),
    LACK_TOKEN(40301, "token缺失"),
    AUTHORIZED_TIMEOUT(40302, "token过期"),
    LOGIN_ERROR(40303, "用户登录失败"),
    NO_LOGGED_IN(40304, "用户未登录"),
    UNAUTHORIZED(40305, "接口未授权"),



    WITHOUT_AUTHORITY(40001, "用户没有权限"),
    ILLEGAL_PARAMETER(40002, "参数非法"),
    USER_FROZEN(40005, "用户已冻结"),
    USER_EMAIL_DUPLICATE(40007, "用户邮箱重复"),
    USER_WITHOUT_DEPARTMENT(40008, "用户没有所属部门"),
    USER_PASSWORD_DUPLICATE(40011, "用户新密码与旧密码重复"),
    USER_EMAIL_NOT_REGISTERED(40012, "该邮箱未注册"),
    ROLE_ASSOCIATE_WITH_USER(40014, "当前角色下存在用户,请为用户分配新角色后删除"),
    ROLE_CODE_DUPLICATE(40015, "角色码重复"),
    DEPARTMENT_ASSOCIATE_WITH_USER(40017, "当前部门或子部门下存在用户,请为用户分配新部门后删除"),
    ILLEGAL_TOKEN(40018, "token非法"),
    AUTHENTICATED_TIMEOUT(40019, "认证超时"),

    DATE_PARSE_ERROR(40021, "解析日期格式错误,请检查日期格式"),
    DEPART_NOT_FOUND(40022, "部门不存在"),
    USER_PASSWORD_IS_INCORRECT(40022, "用户或密码不正确"),
    WORKING_STATUS(40023, "用户已离职或被冻结"),
    ACCOUNT_LOCK(40024, "账号被锁定"),

    // 50xxx表示响应方原因导致的异常
    RUNTIME_EXCEPTION(50000, "运行时异常"),
    SYSTEM_EXCEPTION(50001, "系统异常"),
    REDIS_READ_TIMEOUT(50002, "redis数据读取超时"),
    REDIS_WRITE_TIMEOUT(50003, "redis数据写入超时"),
    MINIO_READ_TIMEOUT(50004, "minio数据读取超时"),
    MINIO_WRITE_TIMEOUT(50005, "minio数据写入超时"),
    MINIO_READ_ERROR(50006, "minio读取数据失败"),
    MINIO_WRITE_ERROR(50007, "minio写入数据失败"),
    DATA_CONNECT_ERROR(50008, "数据库连接失败"),
    MINIO_CONNECT_ERROR(50008, "minio连接失败"),
    MINIO_FILE_NOT_FOUND(500010, "未查询到对应文件记录"),
    MINIO_FILE_NAME_TOO_LONG(50041, "文件名称过长,不能超过127个字符"),
    MINIO_FILE_SUFFIX_ERROR(50042, "文件后缀名非法"),
    MINIO_FILE_SIZE_ERROR(50043, "上传文件过大[单文件大小不得超过:%sMB],如需修改请联系管理员"),
    MINIO_FILE_TOTAL_SIZE_ERROR(50044, "上传文件过大[总上传文件大小不得超过:%sMB],如需修改请联系管理员"),
    FILE_SIZE_ERROR(50045, "上传文件失败"),
    MINIO_PICTURE_FILE_SIZE_ERROR(50046, "上传图片失败,图片大小不得超过:%sMB,如需修改请联系管理员"),

    BEAN_CLONE_ERROR(50010, "克隆bean失败"),
    BEAN_COPY_ERROR(50011, "复制bean失败"),

    DOWNLOAD_ERROR(500020, "下载失败"),

    DOWNLOAD_FILE_EMPTY(500021, "下载附件记录为空"),

    DOWNLOAD_FILE_NOTFOUND(500022, "未找到下载文件或下载文件已删除"),

    // 文件类异常
    ANALYSE_FILE_NAME_ERROR(50021, "解析文件名称失败"),

    // 压缩类异常
    ZIP_FILE_ERROR(50031, "压缩文件失败"),

    SQL_ERROR(50201, "系统异常"),

    MD5_ERROR(50202, "MD5加密失败"),
    URL_PARSE_ERROR(50203, "url编码失败"),

    /**
     * 系统异常信息
     */
    SYSTEM_INSERT_ID_EXIT(60501, "新增系统id必须为空"),
    SYSTEM_UPDATE_ID_EXIT(60502, "更新系统id必须不能为空"),
    SYSTEM_BASE_NOT_EXIT(60503, "新增|修改系统基础信息不存在"),


    ;


    private final int code;

    private final String message;

    ErrorCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public static String getMessageByCode(Integer code) {
        for (ErrorCodeEnum codeEnum : ErrorCodeEnum.values()) {
            if (codeEnum.getCode() == code) {
                return codeEnum.getMessage();
            }
        }
        return "";
    }

}

二、统一异常处理

在基础模块设置(base)

自定义异常类BusinessException.java

package com.lyj.common.base.exceptions;


import com.lyj.common.base.enums.ErrorCodeEnum;
import lombok.Getter;

/**
 *自定义异常
 */
@Getter
public class BusinessException extends RuntimeException {

    private static final long serialVersionUID = 8860475639586178484L;

    private final int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(ErrorCodeEnum errorCode) {
        this(errorCode.getCode(), errorCode.getMessage());
    }

    public BusinessException(String message) {
        this(ErrorCodeEnum.BUSINESS_EXCEPTION.getCode(), message);
    }

}
异常工具类BizExceptionUtil.java
package com.lyj.service.util;


import com.lyj.service.enums.ErrorCodeEnum;
import com.lyj.service.exceptions.BusinessException;

/**
 * 异常工具类
 */
public class BizExceptionUtil {

    private BizExceptionUtil() {
        super();
    }

    /**
     * 抛出异常
     *
     * @param code    异常码
     * @param message 异常信息
     */
    public static void bizException(int code, String message) {
        throw new BusinessException(code, message);
    }

    /**
     * 抛出异常
     *
     * @param errorCode 异常枚举值
     */
    public static void bizException(ErrorCodeEnum errorCode) {
        BizExceptionUtil.bizException(errorCode.getCode(), errorCode.getMessage());
    }

    /**
     * 抛出异常
     *
     * @param message 异常信息
     */
    public static void bizException(String message) {
        BizExceptionUtil.bizException(ErrorCodeEnum.BUSINESS_EXCEPTION.getCode(), message);
    }
}

 统一异常处理GlobalExceptionHandler.java

package com.lyj.common.base.config;


import cn.hutool.core.util.StrUtil;
import com.lyj.common.base.common.R;
import com.lyj.common.base.enums.ErrorCodeEnum;
import com.lyj.common.base.exceptions.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.validation.ConstraintViolationException;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 统一异常处理
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 其他业务异常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public R handle(Exception e) {
        log.error("unexpected Exception", e);
        return R.error(ErrorCodeEnum.BUSINESS_EXCEPTION);
    }
    /**
     * 参数验证失败异常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    public R handle(BindException e) {
        log.error("unexpected BindException", e);
        e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (fieldError == null || StrUtil.isBlank(fieldError.getDefaultMessage())) {
            return R.error(ErrorCodeEnum.PARAMETER_ERROR);
        }
        return R.error(ErrorCodeEnum.PARAMETER_ERROR.getCode(), fieldError.getDefaultMessage());
    }
    /**
     * 参数验证失败异常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(ConstraintViolationException.class)
    public R handle(ConstraintViolationException e) {
        log.error("unexpected BindException", e);
        return R.error(ErrorCodeEnum.PARAMETER_ERROR.getCode(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handle(MethodArgumentNotValidException e) {
        log.error("unexpected MethodArgumentNotValidException", e);
        e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (fieldError == null || StrUtil.isBlank(fieldError.getDefaultMessage())) {
            return R.error(ErrorCodeEnum.PARAMETER_ERROR);
        }
        return R.error(ErrorCodeEnum.PARAMETER_ERROR.getCode(), fieldError.getDefaultMessage());
    }
    /**
     * 处理自定义异常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(BusinessException.class)
    public R handle(BusinessException e) {
        log.error("expected BusinessException,errorMessage={}", e.getMessage());
        return R.error(e.getCode(), e.getMessage());
    }
    /**
     * 文件传输异常
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public R handle(MaxUploadSizeExceededException ex) {
        if (ex.getCause().getCause() instanceof FileSizeLimitExceededException) {
            double actual = BigDecimal.valueOf(((FileSizeLimitExceededException) ex.getCause().getCause()).getActualSize())
                    .divide(BigDecimal.valueOf(1024 * 1024), 3, RoundingMode.HALF_UP).doubleValue();
            double permitted = BigDecimal.valueOf(((FileSizeLimitExceededException) ex.getCause().getCause()).getPermittedSize())
                    .divide(BigDecimal.valueOf(1024 * 1024), 3, RoundingMode.HALF_UP).doubleValue();

            log.error("上传的文件大小: {}MB", actual);
            log.error("允许上传的单个文件大小: {}MB", permitted);
            return R.error(ErrorCodeEnum.MINIO_FILE_SIZE_ERROR.getCode(), String.format(ErrorCodeEnum.MINIO_FILE_SIZE_ERROR.getMessage(), permitted));
        } else if (ex.getCause().getCause() instanceof SizeLimitExceededException) {
            double actual = BigDecimal.valueOf(((FileSizeLimitExceededException) ex.getCause().getCause()).getActualSize())
                    .divide(BigDecimal.valueOf(1024 * 1024), 3, RoundingMode.HALF_UP).doubleValue();
            double permitted = BigDecimal.valueOf(((FileSizeLimitExceededException) ex.getCause().getCause()).getPermittedSize())
                    .divide(BigDecimal.valueOf(1024 * 1024), 3, RoundingMode.HALF_UP).doubleValue();

            log.error("上传的总文件大小: {}MB", actual);
            log.error("允许的总上传文件大小: {}MB", permitted);
            return R.error(ErrorCodeEnum.MINIO_FILE_TOTAL_SIZE_ERROR.getCode(), String.format(ErrorCodeEnum.MINIO_FILE_SIZE_ERROR.getMessage(), permitted));
        }
        log.error("文件上传错误信息: {}", ex.getMessage());
        return R.error(ErrorCodeEnum.FILE_SIZE_ERROR);
    }
}

三、测试

在管理应用模块测试(management)

测试接口testApi.java

package com.lyj.service.management.api;

import com.lyj.common.base.common.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.NotEmpty;

@RestController
@AllArgsConstructor //生成该类下全部属性的构造方法 代替@Autowired 属于lombok
@RequestMapping("/api/test/")
@Validated  //有该@NotEmpty才生效
@Api(tags = "测试API")
public class testApi {

    @GetMapping("/info")
    @ApiOperation("测试info")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "id", required = true)
    })
    public R<Boolean> info(@NotEmpty(message = "id不能为空") @RequestParam("id") String id) {
       return R.ok(Boolean.TRUE);
    }
}

启动项目

访问Knife4j http://localhost:8080/doc.html

异常测试,手动抛出异常

public class testApi {

    @GetMapping("/info")
    @ApiOperation("测试info")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "id", required = true)
    })
    public R<Boolean> info(@NotEmpty(message = "id不能为空") @RequestParam("id") String id) {
        BizExceptionUtil.bizException("系统异常!");
       return R.ok(Boolean.TRUE);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值