大聪明教你学Java | 如何写出优雅的接口

前言

在日常开发中,我们总会写各种各样的接口,尤其是在移动互联网,分布式、微服务盛行的当下,绝大部分项目都采用的微服务框架和前后端分离方式来开发,后端工程师能写出优雅接口代码无疑是前端工程师的一个福音,一个优雅的接口可以拥有良好的可读性,而且在接口出现问题时也可以及时的排查错误原因。那么今天就给大家分享一下大聪明在开发接口时的一些心得。

接口开发

接口规范定义

协议规范

为了确保不同系统/模块间的数据交互,需要事先约定好通讯协议,如:TCP、HTTP、HTTPS协议。为了确保数据交互安全,建议使用HTTPS协议。

接口路径规范

作为接口路径,为了方便清晰的区分来自不同的系统,可以采用不同系统/模块名作为接口路径前缀,咱们举个例子:

  • 支付模块接口路径:/pay/xx
  • 订单模块接口路径:/order/xx

版本控制规范

为了便于后期接口的升级和维护,我们可以在接口路径中加入版本号,便于我们区分和管理接口,从而提升多版本接口的可维护性。不知各位小伙伴有没有留意过,很多框架(比如:Eureka)对外提供的 API 接口中都带有版本号(接口路径中添加类似"v1"、"v2"等版本号)。所以我们在开发接口的时候也可以在接口路径中增加版本号标识,例如 /xx/v1/xx、/xx/v2/xx。

接口命名规范

接口命名和 Java 命名规范一样,遵守一个优雅的接口命名规范,不仅可以增强接口的可读性,而且还会让开发人员之间减少很多不必要的口舌之争(要是接口写的太烂导致开发人员看不懂,备不住就直接爆粗口了)。
我们可以结合上文中写到的【接口路径规范】和【版本控制规范】,外加具体接口命名的方式来编写接口的请求路径。这里建议各位小伙伴在给接口命名的时候也要规范一些,这里我们可以使用“驼峰命名法”按照实现接口的业务类型、业务场景等命名(有必要时可采取多级目录命名,但目录不宜过长,两级目录较为适宜),比如:/user/v1/sys/login,代表的就是版本号为v1的用户服务模块的系统登录接口。在具体接口命名,我们通常会使用以下两种方式

  • 接口名称动词前(后)缀化: 接口名称以接口数据操作的动词为前(后)缀,我们常用的动词有:add、delete、update、query、get、send、save、detail、list等,那么在接口命名的时候就可以写成这样:新建用户 addUser 、查询订单详情 queryOrderDetail。
  • 接口名称动词+请求方式: 接口路径中包含具体接口名称的名词,接口数据操作动作以HTTP请求方式来区分。常用的HTTP请求方式有:GET(从服务器取出资源)、POST(在服务器新建一个资源)、PUT(在服务器更新资源)、DELETE(从服务器删除资源),那么在接口命名的时候我们就可以写成这样:GET /order/v1/orders:列出所有订单、POST /order/v1/orders:新建一个订单。

请求参数规范

  • 请求方式: 按照GET、POST、PUT等含义定义,避免出现不一致现象,防止造成误解。
  • 请求头: 请求头根据项目需求添加配置参数。如:请求数据格式,accept="application/json"等。如有需要,请求头可根据项目需求要求传入用户token、唯一验签码等加密数据。
  • 请求参数/请求体: 请求参数字段,尽可能与数据库表字段、对象属性名等保持一致,因为保持一致最省事,最舒服的一件事。

返回数据规范

统一规范返回数据的格式,对己对彼都有好处,此处以json格式为例。返回数据应包含:返回状态码、返回状态信息、具体数据。格式如下

{
    "status":"xxx",
    "msg":"xxx",
    "data": {
        //json格式的具体数据
    }
}

上面我们提到了状态码,那么我们再具体的谈一谈关于状态码的规范。一个优雅的接口,给我们提供了简洁明了的状态码,根据状态码就可以让我们快速的定位问题根源所在。我们采用 Http 的状态码进行数据封装,其中状态码为 200 就表示请求成功,状态码为 4xx 就表示客户端错误,状态码为 5xx 就表示服务器内部发生错误。状态码设计参考如下:

public enum CodeEnum {

    // 根据具体业务需求进行添加
    SUCCESS(200,"请求成功"),
    ERROR_PATH(404,"请求地址未找到"),
    ERROR_SERVER(500,"服务器内部发生错误");
    
    private int code;
    private String message;
    
    CodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

我们刚刚也提到了“返回数据应包含:返回状态码、返回状态信息、具体数据”,那么在这里也给大家贴上一个本人常用的返回结果类及其常用方法

public class AjaxResult extends HashMap<String, Object>{

    private static final long serialVersionUID = 1L;

    /** 状态码 */
    public static final String CODE_TAG = "code";

    /** 返回内容 */
    public static final String MSG_TAG = "msg";

    /** 数据对象 */
    public static final String DATA_TAG = "data";

    /**
     * 状态类型
     */
    public enum Type
    {
        /** 成功 */
        SUCCESS(0),
        /** 警告 */
        WARN(301),
        /** 错误 */
        ERROR(500);
        private final int value;

        Type(int value)
        {
            this.value = value;
        }

        public int value()
        {
            return this.value;
        }
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult()
    {
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param type 状态类型
     * @param msg 返回内容
     */
    public AjaxResult(Type type, String msg)
    {
        super.put(CODE_TAG, type.value);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param type 状态类型
     * @param msg 返回内容
     * @param data 数据对象
     */
    public AjaxResult(Type type, String msg, Object data)
    {
        super.put(CODE_TAG, type.value);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 方便链式调用
     *
     * @param key 键
     * @param value 值
     * @return 数据对象
     */
    @Override
    public AjaxResult put(String key, Object value)
    {
        super.put(key, value);
        return this;
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success()
    {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
        return new AjaxResult(Type.SUCCESS, msg, data);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult warn(String msg)
    {
        return AjaxResult.warn(msg, null);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult warn(String msg, Object data)
    {
        return new AjaxResult(Type.WARN, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @return
     */
    public static AjaxResult error()
    {
        return AjaxResult.error("操作失败");
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(Type.ERROR, msg, data);
    }
}

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西

你可能感兴趣的:(独乐乐不如众乐乐,java,开发语言,接口,代码规范)