controller层的处理

controller层的处理

controller请求 发送之后 为了更加规范 我们一般会对返回的数据进行封装 下面就来一步步进行封装

统一返回的数据

基本使用

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {
    private  String code;
    private  String message;
    private  Object object;

    /**
     * 成功返回结果
     * @param message
     * @return
     */
    public  static RespBean success(String message){
        return  new RespBean(CommonEnum.SUCCESS.getResultCode(), message,null);
    }

    // 默认返回成功状态码,数据对象
    public static RespBean success(Object data) {
        return  new RespBean(CommonEnum.SUCCESS.getResultCode(),CommonEnum.SUCCESS.getResultMsg(),data);
    }
    /**
     * 成功返回结果
     * @param message
     * @param obj
     * @return
     */
    public  static RespBean success(String message, Object obj){
        return  new RespBean(CommonEnum.SUCCESS.getResultCode(), message,obj);
    }

    /**
     * 失败返回结果
     * @param commonEnum
     * @return
     */
    public  static RespBean error(CommonEnum commonEnum){
        return  new RespBean(commonEnum.getResultCode(), commonEnum.getResultMsg(),null);
    }
    /**
     * 失败返回结果
     * @param commonEnum
     * @return
     */
    public  static RespBean error(CommonEnum commonEnum,Object object){
        return  new RespBean(commonEnum.getResultCode(), commonEnum.getResultMsg(),object);
    }
    /**
     * 失败返回结果
     * @param message
     * @return
     */
    public  static RespBean error(String message){
        return  new RespBean(CommonEnum.INTERNAL_SERVER_ERROR.getResultCode(), message,null);
    }

    /**
     * 失败返回结果
     * @param message
     * @param obj
     * @return
     */
    public  static RespBean error(String message, Object obj){
        return  new RespBean(CommonEnum.INTERNAL_SERVER_ERROR.getResultCode(), message,obj);
    }
}

image-20220817143140740

code为状态码 messages 是消息 object为前端要的数据

这些状态码当然不是直接手动写上去的呀 不然得多麻烦 而且万一要是搞混了 没有统一的标准 那 200是成功 300也是成功 前端根本搞不明白你在干什么

所以为了方便管理和协同开发 一般都是定好了相关的枚举类

在这之前 为了代码的规范 定义一个统一的标准 我们先定义一个状态码的接口

public interface StatusCode {
    public int getCode();
    public String getMsg();
}

然后写一个状态的枚举类实现StatusCode

/**
 * 自定义的状态描述枚举类
 * @author chenyaoyan
 * @version
 * @since 2022-08-17 13:50:57

 */
public  enum CommonEnum implements StatusCode {
// 数据操作错误定义
    SUCCESS("200", "成功!"),
    VALIDATE_ERROR("400", "参数校验失败,请求的数据格式不符"),
    SIGNATURE_NOT_MATCH("401","客户端请求未授权!"),
    NOT_FOUND("404", "未找到该资源!"),
    INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
    SERVER_BUSY("503","服务器正忙,请稍后再试!"),
    METHOD_NOT_SUPPORTED("405", "不支持当前请求方法"),
    MEDIA_TYPE_NOT_SUPPORTED("415", "不支持当前媒体类型"),
    REQ_REJECT("403", "没有权限,请求被拒绝"),
    RESPONSE_PACK_ERROR("1003","response返回包装失败");

    /** 错误码 */
    private String resultCode;

    /** 错误描述 */
    private String resultMsg;

    CommonEnum(String resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }

    @Override
    public String getResultCode() {
        return resultCode;
    }

    @Override
    public String getResultMsg() {
        return resultMsg;
    }

}

然后我们就可以使用他了

在controller的方法中 new一个 RespBean 然后里面丢入 CommonEnum和数据 并返回

统一异常处理

有时候系统自己的异常 比如 sql 语句的错误 空指针异常什么的 这个时候如果我们手动的一个个捕捉异常并且封装好 你自己觉得不累吗 幸好 spring boot 有一个@RestControllerAdvice可以供我们使用

我们先定义好自己系统的业务的异常枚举类 然后实现状态码接口

@Getter
public  enum  BizCode implements StatusCode {
    APP_ERROR("2000", "业务异常");


    private String code;
    private String msg;
    BizCode(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    @Override
    public String getResultCode() {
        return code;
    }

    @Override
    public String getResultMsg() {
        return msg;
    }
}

自定义异常类

/**
 * 自定义业务异常
 * @author chenyaoyan
 * @version
 * @since 2022-08-17 13:50:57
 */
@Getter
public class BizException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    /**
     * 错误码
     */
    protected String errorCode;
    /**
     * 错误信息
     */
    protected String errorMsg;

    // 手动设置异常
    public BizException(StatusCode statusCode,String message) {
        super(message);
        this.errorCode = statusCode.getResultCode();
        this.errorMsg = statusCode.getResultMsg();
    }

    public BizException(StatusCode statusCode, Throwable cause) {
        super(statusCode.getResultMsg(), cause);
        this.errorCode = statusCode.getResultCode();
        this.errorMsg = statusCode.getResultMsg();
    }

    public BizException(String errorMsg) {
        super(errorMsg);
        this.errorCode = BizCode.APP_ERROR.getCode();
        this.errorMsg = BizCode.APP_ERROR.getMsg();
    }
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
}

然后统一的进行拦截

SpringBoot全局异常处理方式主要两种:
使用 @ControllerAdvice 和 @ExceptionHandler 注解。
使用 ErrorController类 来实现
区别:
1. @ControllerAdvice 方式只能处理控制器抛出的异常。此时请求已经进入控制器中。
@ControllerAdvice :表示这是一个控制器增强类,当控制器发生异常且符合类中定义的拦截异
常类,将会被拦截
@ExceptionHandler :定义拦截的异常类
2. ErrorController类 方式可以处理所有的异常,包括未进入控制器的错误,比如404,401等错误
3. 如果应用中两者共同存在,则 @ControllerAdvice 方式处理控制器抛出的异常,
ErrorController类 方式处理未进入控制器的异常。
4. @ControllerAdvice 方式可以定义多个拦截方法,拦截不同的异常类,并且可以获取抛出的异常
信息,自由度更大。

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 参数异常
     * @param e
     * @return
     */
    @ExceptionHandler({BindException.class})
    public RespBean MethodArgumentNotValidExceptionHandler(BindException e) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        return  RespBean.error(CommonEnum.VALIDATE_ERROR, objectError.getDefaultMessage());
    }

    /**
     * 处理自定义的业务异常
     * @param e
     * @return
     */
    @ExceptionHandler(BizException.class)
    public RespBean APIExceptionHandler(BizException e) {
        logger.error(e.getMessage());
        return new RespBean(e.getErrorCode(), e.getErrorMsg(), e.getMessage());
    }

    /**
     * 处理空指针的异常

     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public RespBean exceptionHandler(NullPointerException e){
        logger.error("发生空指针异常!原因是:");
        return RespBean.error(CommonEnum.INTERNAL_SERVER_ERROR,"空指针异常");
    }

    /**
     * 处理数据库异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = SQLException.class)
    public RespBean mySQLException(SQLException e) {
        if (e instanceof SQLIntegrityConstraintViolationException) {
            return RespBean.error("该数据有与之相关数据,操作失败!",e);
        }
        return RespBean.error("数据库异常,操作失败!",e);
    }

    /**
     * 处理其他异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public RespBean exceptionHandler(HttpServletRequest req, Exception e){
        logger.error("未知异常!原因是:",e);
        return RespBean.error(CommonEnum.INTERNAL_SERVER_ERROR.getResultMsg());
    }


}

统一响应

如果每次写一个controller中的方法 都要去封装一次返回的类 那多类啊 为了偷懒和实现统一效应

/**
 * @author chenyaoyan
 * @version 1.0
 * @Description 统一响应,拦截处理RestController
 * @date 2022-08-17 13:50:57
 */
@RestControllerAdvice(basePackages = {"com.cyy.test.controller"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        // response是RespBean类型,或者注释了NotControllerResponseAdvice都不进行包装
        return !(methodParameter.getParameterType().isAssignableFrom(RespBean.class)
                || methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.class));
    }

    @Override
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
        // String类型不能直接包装
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 将数据包装在ResultVo里后转换为json串进行返回
                return objectMapper.writeValueAsString(RespBean.success(data));
            } catch (JsonProcessingException e) {
                throw new BizException(CommonEnum.RESPONSE_PACK_ERROR, e.getMessage());
            }
        }
        // 否则直接包装成RespBean返回
        return RespBean.success(data);
    }
}

①@RestControllerAdvice(basePackages = {"com.cyy.test.controller"}) 自动扫描了所有指定包下的 controller,在 Response 时进行统一处理。

②重写 supports 方法,也就是说,当返回类型已经是 ResultVo 了,那就不需要封装了,当不等与 ResultVo 时才进行调用 beforeBodyWrite 方法,跟过滤器的效果是一样的。

③最后重写我们的封装方法 beforeBodyWrite,注意除了 String 的返回值有点特殊,无法直接封装成 json,我们需要进行特殊处理,其他的直接 new ResultVo(data); 就 ok 了。

有些时候 如果我们不想要自动响应返回

那也好办

因为只有一部分不要统一响应返回 但是如果一个个过滤又很麻烦

所以定义一个注解 然后使用注解 在统一返回的时候筛选一下 如果又这个注解则不响应就可以了

注解定义

/**
 * @author chenyaoyan
 * @version 1.0
 * @Description 自定义元注解 放在RestController的方法上 如果使用 则不自动包装返回类
 * @date 2022-08-17 13:50:57
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
}

然后像上面那样过滤掉 就可以了

posted @ 2022-09-06 00:53  是垚不是瑶  阅读(146)  评论(0)    收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示