关于java:年轻人不讲武德竟然重构出这么优雅后台-API-接口

31次阅读

共计 4616 个字符,预计需要花费 12 分钟才能阅读完成。

Hello,早上好,我是楼下小黑哥~

最近偶然间在看到 Spring 官网文档的时候,新学到一个注解 @ControllerAdvice,并且胜利应用这个注解重构咱们我的项目的对外 API 接口,去除繁琐的反复代码,使其开发更加优雅。

展现具体重构代码之前,咱们先来看下原先对外 API 接口是如何开发的。

这个 API 接口次要是用来与咱们 APP 交互,这个过程咱们对立定义一个交互协定,APP 端与后盾 API 接口对立都应用 JSON 格局。

另外后盾 API 接口对 APP 返回时,对立一些错误码,APP 端须要依据相应错误码,在页面弹出一些提醒。

上面展现一个查问用户信息返回的接口数据:

{
    "code": "000000",
    "msg": "success",
    "result": {
        "id": "1",
        "name": "test"
    }
}

code 代表对外的错误码,msg 代表错误信息,result 代表具体返回信息。

前端 APP 获取这个返回信息,首先判断接口返回 code 是否为 000000,如果是代表查问胜利,而后获取 result 信息作出相应的展现。否则,间接弹出相应的错误信息。

欢送关注我的公众号:程序通事,取得日常干货推送。如果您对我的专题内容感兴趣,也能够关注我的博客:studyidea.cn

重构之前

上面咱们来看下,重构之前的,后盾 API 层的如何编码。

/**
 * V1 版本
 *
 * @return
 */
@RequestMapping("testv1")
public APIResult testv1() {
    try {User user = new User();
        user.setId("1");
        user.setName("test");
        return APIResult.success(user);
    } catch (APPException e) {log.error("外部异样", e);
        return APIResult.error(e.getCode(), e.getMsg());
    } catch (Exception e) {log.error("零碎异样", e);
        return APIResult.error(RetCodeEnum.FAILED);
    }
}

下面的代码其实很简略,外部对立封装了一个工具类 APIResult,而后用其包装具体的后果。

@Data
public class APIResult<T> implements Serializable {

    private static final long serialVersionUID = 4747774542107711845L;

    private String code;

    private String msg;

    private T result;


    public static <T> APIResult success(T result) {APIResult apiResult = new APIResult();
        apiResult.setResult(result);
        apiResult.setCode("000000");
        apiResult.setMsg("success");
        return apiResult;
    }

    public static APIResult error(String code, String msg) {APIResult apiResult = new APIResult();
        apiResult.setCode(code);
        apiResult.setMsg(msg);
        return apiResult;
    }

    public static APIResult error(RetCodeEnum codeEnum) {APIResult apiResult = new APIResult();
        apiResult.setCode(codeEnum.getCode());
        apiResult.setMsg(codeEnum.getMsg());
        return apiResult;
    }

除了这个以外,还定义一个异样对象 APPException,用来对立包装外部的各种异样。

下面的代码很简略,然而呢能够说比拟繁琐,反复代码也比拟多,每个接口都须要应用 try...catch 包装,而后应用 APIResult 包含失常的返回信息与错误信息。

第二呢,接口对象只能返回 APIResult,实在业务对象只能暗藏在 APIResult 中。这样不太优雅,另外不能很直观晓得实在业务对象。

重构之后

上面咱们开始重构下面的代码,次要目标是去除反复的那一坨 try...catch 代码。

这次重构咱们须要应用 Spring 注解 @ControllerAdvice 以及 ResponseBodyAdvice,咱们先来看下重构的代码。

ps: ResponseBodyAdvice 来自 Spring 4.2 API,如果各位同学须要应用这个的话,可能须要降级 Spring 版本。

改写返回信息

首先咱们须要实现 ResponseBodyAdvice,实现咱们本人的解决类。

@ControllerAdvice
public class CustomResponseAdvice implements ResponseBodyAdvice {
    /**
     * 是否须要解决返回后果
     * @param methodParameter
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {System.out.println("In supports() method of" + getClass().getSimpleName());
        return true;
    }

    /**
     * 解决返回后果
     * @param body
     * @param methodParameter
     * @param mediaType
     * @param aClass
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {System.out.println("In beforeBodyWrite() method of" + getClass().getSimpleName());
        if (body instanceof APIResult) {return body;}
        return APIResult.success(body);
    }
}

实现下面的接口,咱们就能够在 beforeBodyWrite 办法里,批改返回后果了。

下面代码中,只是简略应用 APIResult 包装了返回后果,而后返回。其实咱们还能够在此减少一些额定逻辑,比如说如接口返回信息由加密的需要,咱们能够在这一层对立加密。

另外,这里判断一下 body 是否 APIResult 类,如果是就间接返回,不做批改。

这么做一来兼容之前的老接口,这是因为默认状况下,咱们本人实现的 CustomResponseAdvice 类,将会对所有的 Controller 失效。

如果不做判断,以前的老接返回就会被包装了两层 APIResul, 影响 APP 解析。

除此之外,如果大家放心这个批改对以前的老接口有影响的话,能够应用上面的形式,只对指定的办法失效。

首先自定义一个注解,比如说:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomResponse {}

而后将其标注在须要改变的办法中,而后咱们在 ResponseBodyAdvice#supports 中判断具体方法上有没有自定义注解 CustomResponse,如果存在,返回 true,这就代表最初将会批改返回类。如果不存在,则返回 false,那么就会跟以前流程一样。

/**
 * 是否须要解决返回后果
 *
 * @param methodParameter
 * @param aClass
 * @return
 */
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {System.out.println("In supports() method of" + getClass().getSimpleName());
    Method method = methodParameter.getMethod();
    return method.isAnnotationPresent(CustomResponse.class);
}

全局异样解决

下面的代码重构之后,将反复代码抽取了进去,整体的代码就剩下咱们的业务逻辑,这样就变得十分简洁优雅。

不过,下面的重构的代码,还是存在问题,次要是异样的解决。

如果下面的业务代码抛出了异样,那么接口将会返回堆栈错误信息,而不是咱们定义的错误信息。所以上面咱们这个,再次优化一下。

这次咱们次要须要应用 @ExceptionHandler 注解,这个注解须要与 @ControllerAdvice 一起应用。

@Slf4j
@ControllerAdvice
public class CustomExceptionHandler {@ExceptionHandler(Exception.class)
    @ResponseBody
    public APIResult handleException(Exception e) {log.error("零碎异样", e);
        return APIResult.error(RetCodeEnum.FAILED);
    }

    @ExceptionHandler(APPException.class)
    @ResponseBody
    public APIResult handleAPPException(APPException e) {log.error("外部异样", e);
        return APIResult.error(e.getCode(), e.getMsg());
    }

}

应用这个 @ExceptionHandler,将会拦挡相应的异样,而后将会调用的相应办法解决异样。这里咱们就应用 APIResult 包装一些错误信息返回。

总结

咱们能够应用 @ControllerAdviceResponseBodyAdvice 拦挡返回后果,对立做出一些批改。这样就能够应用的业务代码十分简洁,优雅。

另外,针对业务代码的中,咱们能够应用 @ExceptionHandler 注解,对立做一个全局异样解决,这样就能够无缝的跟 ResponseBodyAdvice 联合。

不过这里须要一点,咱们实现的 ResponseBodyAdvice 类,肯定须要跟 @ControllerAdvice 配合一起应用哦,至于具体起因,下篇文章小黑哥剖析原来的时候,再具体解释哦。敬请期待哦~

欢送关注我的公众号:程序通事,取得日常干货推送。如果您对我的专题内容感兴趣,也能够关注我的博客:studyidea.cn

正文完
 0