专栏首页纯洁的微笑年轻人不讲武德,竟然重构出这么优雅后台 API 接口

年轻人不讲武德,竟然重构出这么优雅后台 API 接口

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 信息作出相应的展示。否则,直接弹出相应的错误信息。

重构之前

下面我们来看下,重构之前的,后台 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配合一起使用哦,至于具体原因,下篇文章阿粉分析原来的时候,再具体解释哦。敬请期待哦~

本文分享自微信公众号 - 纯洁的微笑(keeppuresmile),作者:鸭血粉丝

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-11-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 年轻人不讲武德,竟然重构出这么优雅后台 API 接口

    最近偶然间在看到 Spring 官方文档的时候,新学到一个注解 @ControllerAdvice,并且成功使用这个注解重构我们项目的对外 API 接口,去除繁...

    andyxh
  • 写了一套优雅接口之后,领导让我给大家讲讲这背后的技术原理

    上周文章年轻人不讲武德,竟然重构出这么优雅后台 API 接口我们使用 @ControllerAdvice与 ResponseBodyAdvice 重构后端的 ...

    andyxh
  • 喜马拉雅小雅Nano首发,仅需1元即可预约限量抢购

    今天,喜马拉雅上线首发小雅Nano,喜欢智能音箱的粉丝们又有福利了。小雅Nano是一款能免费听喜马拉雅会员精品的AI音箱产品。想要第一时间尝鲜的话很简单,只要在...

    曾响铃
  • 发布高端产品G5,还要把旗舰店开遍美国和欧洲 雅迪葫芦里卖的什么药?

    好的产品,应该是能够引领行业潮流。在前不久,道琼斯旗下财经媒体《MarketWatch》就大篇幅的报道了中国品牌雅迪在米兰国际摩托车展发布面向高端市场的智能锂电...

    曾响铃
  • 2014年18张最佳数据可视化图表

    大数据文摘
  • 万人迷

    他,是一个万人迷。 低调,奢华,有内涵。走在名叫“微信”的大街上,有一万个人能认出他来;放个屁,能让一万个人同时捂住鼻子。 这一天,是公元2015年5月15日,...

    腾讯数据中心
  • 如何写一手漂亮的 Vue

    前几日听到一句生猛与激励并存,可怕与尴尬同在,最无奈也无解的话:“90后,你的中年危机已经杀到”。这令我很受触动。显然,这有些夸张了,但就目前这日复一日的庸碌下...

    晚晴幽草轩轩主
  • 程序员,你需要些“脑力运动”了

    程序员,你需要些“脑力运动”了 [读书笔记]开发你的大脑--图像记忆法 1   前言 最近看了一本关于记忆的书,叫《超级记忆力-图像记忆法》,看完之后发现一些有...

    用户1170933
  • 硬件拆解:小武机器人众筹破百万的秘密在哪里?

    我们今天看到的这款小武机器人,由深圳市寒武纪智能科技有限公司(一下简称寒武纪智能)研发制造,是一个主打拍照和内容交互的教育陪伴机器人。从2014年9月开始技术准...

    机器人网
  • 分析了35个大城市,年轻人究竟该选哪里安顿自己?

    当年轻人几乎跟“创新”和“未来”成为同义词,如何吸引并留住他们成为热门议题。城市在这件事上做了很多努力,加码人才引进政策、鼓励创新企业、翻新城市形象……期待某一...

    CDA数据分析师
  • 2018中国运营增长大会:2大城市,30+实战派大咖和你一起“引爆”增长引擎

    ? 一日一变的互联网世界,今年竞争异常激烈 从直播答题,人工智能,区块链, 短视频,P2P 无数产品诞生、火爆、变革和死亡 多变的一年,流量红利期已过,微信...

    腾讯大讲堂
  • 重塑微软:全面人工智能和变酷的新故事

    大数据文摘
  • 城市魅力排行榜丨这座城市竟然超越北上广!

    中国城市的进化并不是只有一条确定的道路,沿着自己最擅长的轨迹,才会成为人们心中那个特别的城市。依据最新一年的160个品牌商业数据、17家互联网公司的用户行为数据...

    钱塘数据
  • 1955-2019:从钱学森之问到AI突围

    导语:大国超级工程的背后,都有大规模的人才战略推动,人才战略带来技术突破,技术突破再形成一个行业的黄金时代。

    AI科技大本营
  • 特稿丨平安肖京:AI 的差距、边界、传承

    雷锋网AI掘金志按:AI圈向来有个传统,倾向将不同的人按界分类为工业界、学术界等。但真的要给肖京打标签时,我却犯了糊涂。

    AI掘金志
  • 中国程序员人数将增长50%,过半500强都要卖软件,边缘应用程序增长8倍丨IDC预测下一个五年

    发布之前,IDC中国副总裁兼首席分析师武连峰先讲了讲2020年的宏观政经环境,依然不那么乐观,贸易摩擦成为新常态,中欧技术合作曲曲折折,WTO作用日趋减弱。

    量子位
  • WGDC大会再次登陆,带你玩转空间大数据

    WGDC是由泰伯网主办的地理信息领域最具影响力的技术创新盛会,每年都会吸引全球各地的朋友参加。今年大会将于6月13日-14日举办,将吸引8000多人次、200多...

    DT数据侠
  • 【一】从0开始,用flask+mongo打造分布式服务器监控平台

    在前面一篇文章《 Python 系统资源信息获取工具,你用过没?》中,我们学习了如何使用 Python 中的第三方库 psutil 来获取系统的资源信息,比如 ...

    崔庆才
  • 高举高打快速渗透77个国家,雅迪电动能成为两轮界的“华为”吗?

    近期,路透社、华盛顿商业周刊在内的一大波外媒,集中报道了中国电动车品牌雅迪在意大利米兰国际摩托车展上发布的一款面向高端市场的智能锂电两轮车G5。这款售价3299...

    用户2908108

扫码关注云+社区

领取腾讯云代金券