统一异常处理与统一信息返回

统一异常处理与信息返回

创建统一返回类
package com.example.myweb.common;

import com.example.myweb.exception.AppExceptionCodeMsg;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {

    /**
     * 服务端返回的状态码
     */
    private int code;
    /**
     * 服务端返回的信息
     */
    private String msg;
    /**
     * 服务端返回的数据
     */
    private T data;

    /**
     * 请求成功,返回状态码为200(请求成功时,状态码均为200),返回信息无需输入(默认为success),返回数据手动输入
     * @param data
     * @param <T>
     * @return
     */
    public static <T> Result success(T data){
        return new Result(200, "success", data);
    }

    /**
     * 请求成功,返回状态码为200(请求成功时,状态码均为200),返回信息手动输入,返回数据手动输入
     * @param msg
     * @param data
     * @param <T>
     * @return
     */
    public static <T> Result success(String msg,T data){
        return new Result(200,msg, data);
    }

    /**
     * 请求出错,返回状态码手动输入,返回信息手动输入,返回数据为空(请求出错时不需要返回任何数据)
     * @param code
     * @param msg
     * @param <T>
     * @return
     */
    public static <T> Result error(int code,String msg){
        return new Result(code,msg, null);
    }
}
创建业务异常枚举类
package com.example.myweb.exception;

/**
 * 异常枚举类,枚举出所有可能出现的和业务相关的异常
 */
public enum AppExceptionCodeMsg {

    INVALID_CODE(10000,"验证码无效"),
    USERNAME_NOT_EXISTS(10001,"用户不存在"),
    USER_LOGIN_ACCOUNT(10002,"用户名或者密码错误"),
    USER_PERMISSION_NOT_ENOUTH(10003,"用户权限不足"),
    USER_EMPTY(10004,"用户名为空"),
    USER_EXISTS(10006,"用户名已存在"),
    USER_DELETE_FAIL(11001,"删除失败"),
    USER_REGISTER_FAIL(11000,"用户注册失败"),

    USER_AUTHENTICATION_NO_TOKEN(401,"Token为空"),
    USER_AUTHENTICATION_NO_USER(401,"用户不存在"),
    USER_AUTHENTICATION_TOKEN_EXPIRE(401,"token已过期"),
    USER_AUTHENTICATION_TOKEN_FAILED(401,"token验证失败");
    ;

    private int code ;
    private String msg ;

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    AppExceptionCodeMsg(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

}
创建自定义异常类
package com.example.myweb.exception;

import lombok.Data;

/**
 * 自定义异常,根据业务逻辑手动抛出此异常:throw new AppException();
 * 异常抛出后被全局异常处理类(用@ControllerAdvice注解修饰):GlobalExceptionHandler接收并作出相应的处理
 * Intellij IDEA中Java类图标为闪电符号说明:这是一个Exception类的子类
 */
@Data
public class AppException extends RuntimeException{
    /**
     * 根据业务逻辑返回错误状态码
     */
    private int code;
    /**
     * 根据业务逻辑返回错误信息
     */
    private String msg;

    /**
     * 传入的参数为异常枚举类AppExceptionCodeMsg中枚举的异常(返回状态码和返回信息),可能出现的异常被异常枚举类集中管理
     * @param appExceptionCodeMsg
     */
    public AppException(AppExceptionCodeMsg appExceptionCodeMsg){
        super();
        this.code = appExceptionCodeMsg.getCode();
        this.msg = appExceptionCodeMsg.getMsg();
    }

    /**
     * 传入的参数为异常枚举类AppExceptionCodeMsg中未枚举的异常
     * @param code
     * @param msg
     */
    public AppException(int code,String msg){
        super();
        this.code = code;
        this.msg = msg;
    }
}
创建全局异常处理类
package com.example.myweb.exception;

import com.example.myweb.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 全局异常处理类,spingboot中出现的所有异常都会被此类拦截捕获
 * @ControllerAdvice注解修饰的类为全局异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = {Exception.class}) //@ExceptionHandler注解声明了全局异常处理类中对异常处理的方法
    @ResponseBody //此注解表示此类中return的返回值会直接被填入HTTP响应体中,然后返回给前端
    public <T> Result<T> exceptionHandler(Exception e){
        //对出现的异常进行判断,根据不同类型的异常进行不同方式的处理
        //此处为判断拦截到的Exception是不是我们自定义的异常类型
        if(e instanceof AppException){
            AppException appException = (AppException)e;
            return Result.error(appException.getCode(),appException.getMsg());
        }

        //如果拦截的异常不是我们自定义的异常(非业务相关的异常),则返回服务器异常
        return Result.error(500,"服务器异常");
    }
}
创建controller类
package com.example.myweb.controller;

import com.example.myweb.common.Result;
import com.example.myweb.exception.AppException;
import com.example.myweb.exception.AppExceptionCodeMsg;
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;

@RestController
@RequestMapping("/demo")
public class UserController {
    @GetMapping("/test")
    public Result<String> demoTest(@RequestParam("username") String username ){
        if("Lucy".equals(username)){
            return Result.success("success",null);
        }
        if("Han".equals(username)){
            return Result.success("Welcome Han!","Are you ok?");
        }
        if("Sun".equals(username)){
            throw new AppException(AppExceptionCodeMsg.USERNAME_NOT_EXISTS);
        }
        if("Jack".equals(username)){
            throw new AppException(AppExceptionCodeMsg.USER_PERMISSION_NOT_ENOUTH);
        }
        if("Rose".equals(username)){
            int a = 1/0;
        }
        if("".equals(username)||username == null){
            throw new AppException(AppExceptionCodeMsg.USER_EMPTY);
        }
        return Result.success("default",null);
    }
}
测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当不输入username为null时,即不传递参数username时,并不会进入到逻辑:

    if("".equals(username)||username == null){
        throw new AppException(AppExceptionCodeMsg.USER_EMPTY);
    }

中,因为@RequestParam(“username”)注解规定了必须要传入username参数,否则会出现异常:

org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'username' for method parameter type String is not present

,而我们在全局异常处理类中,对除了我们自定义异常AppException之外的其他异常并没有做相应的处理,所以会执行逻辑:

return Result.error(500,"服务器异常");

使用validation做参数校验

导入依赖

springboot2.3版本之前spring-boot-starter-web中携带validation依赖,2.3版本及其之后需要单独导入validation依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
常用的字段验证的注解

https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485783&idx=1&sn=a407f3b75efa17c643407daa7fb2acd6&chksm=cea2469cf9d5cf8afbcd0a8a1c9cc4294d6805b8e01bee6f76bb2884c5bc15478e91459def49&token=292197051&lang=zh_CN#rd

所有的注解,推荐使用 JSR 注解,即javax.validation.constraints,而不是org.hibernate.validator.constraints

  • JSR提供的校验注解:

    • @Null 被注释的元素必须为 null
    • @NotNull 被注释的元素必须不为 null
    • @AssertTrue 被注释的元素必须为 true
    • @AssertFalse 被注释的元素必须为 false
    • @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    • @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    • @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    • @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    • @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
    • @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
    • @Past 被注释的元素必须是一个过去的日期
    • @Future 被注释的元素必须是一个将来的日期
    • @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式

    Hibernate Validator提供的校验注解

    • @NotBlank(message =) 验证字符串非null,且长度必须大于0
    • @Email 被注释的元素必须是电子邮箱地址
    • @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
    • @NotEmpty 被注释的字符串的必须非空
    • @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
对实体类使用校验注解
package com.example.myweb.controller.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.NotEmpty;

/**
 * dto( data transfer object):数据传输对象,service 或 manager 向外传输的对象。
 */
@Data
public class UserDTO {
    @NotEmpty(message = "username不能为空")
    private String username;
    @NotEmpty(message = "password不能为空")
    private String password;
}
完善全局异常处理类
package com.example.myweb.exception;

import com.example.myweb.common.Result;
import org.springframework.validation.BindingResult;
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 java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理类,spingboot中出现的所有异常都会被此类拦截捕获
 * @ControllerAdvice注解修饰的类为全局异常处理类
 */
@ControllerAdvice
@ResponseBody //此注解表示此类中return的返回值会直接被填入HTTP响应体中,然后返回给前端
public class GlobalExceptionHandler {

    //对出现的异常进行判断,根据不同类型的异常进行不同方式的处理,此处为拦截我们自定义异常:AppException
    @ExceptionHandler(value = {AppException.class}) //@ExceptionHandler注解声明了全局异常处理类中对异常处理的方法,value = {AppException.class}表示捕获AppEcception类型的异常
    public <T> Result<T> appExceptionHandler(AppException e){
        return Result.error(e.getCode(),e.getMsg());
    }

    //处理MethodArgumentNotValidException异常,此异常为validation抛出的异常
    @ExceptionHandler(value={MethodArgumentNotValidException.class})
    public <T> Result<T> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
        BindingResult bindingResult = e.getBindingResult();
        String errorMesssage = "";
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            errorMesssage += fieldError.getDefaultMessage() + "!";
        }
        return Result.error(2000,errorMesssage);
    }

    //处理不属于上述异常的异常
    @ExceptionHandler(value = {Exception.class})
    public <T> Result<T> exceptionHandler(Exception e){
        e.printStackTrace();
        return Result.error(500,"服务器异常:"+e.getMessage());
//        return Result.error(500,"服务器异常");
    }
}
创建controller类

参数上一定要加上@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException

类上一定要加上 Validated 注解,这个注解可以告诉 Spring 去校验方法参数。

package com.example.myweb.controller;

import com.example.myweb.common.Result;
import com.example.myweb.controller.dto.UserDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("/demo")
@Validated
public class UserController {
    @PostMapping("/test2")
    public Result<UserDTO> test2(@RequestBody @Valid UserDTO userDTO){
        return Result.success(userDTO);
    }
}
测试

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值