写点什么

编写令人愉悦的 API 接口 (一)

用户头像
陈云轩
关注
发布于: 2020 年 12 月 23 日
编写令人愉悦的API接口(一)

引言

API 接口是服务端与客户端沟通的桥梁.较好的 API 设计能减少客户端与服务端的联调时间,更加关注于自己本身代码的优化与业务层的逻辑.

API 设计知识点

API 组成

良好的 API 接口应该从这下面几个方向进行优化

  1. 准确的 API 协议

  2. 准确的内容类型

  3. 统一的返回类型以及异常处理

  4. 良好的接口版本控制体系

  5. API 接口路径尽量简短统一

  6. 性能与安全


实践

API 协议类型划分

GET : 从服务器上获取一个具体的资源或者一个资源列表。

POST : 在服务器上创建一个新的资源。

PUT : 以整体的方式更新服务器上的一个资源。

PATCH : 只更新服务器上一个资源的一个属性。

DELETE : 删除服务器上的一个资源。

HEAD : 获取一个资源的元数据,如数据的哈希值或最后的更新时间。

OPTIONS : 获取客户端能对资源操作的信息。


其中GET,POST,PUT,PATCH,DELETE这五种协议在日常 CRUD 开发中最为常用

以用户模块的业务场景分析


//获取用户列表(分页)@GetMapping(value = "user")public R selectList(UserSearch userSearch) {    //userSearch 是一个搜索实体,里面有页码以及筛选条件属性    return ResultUtil.data();}//获取单个用户信息@GetMapping(value = "user/{id}")public R selectOne(@PathVariable("id") String id) {    //获取用户信息,与分页接口相同采用GET协议,用path传值id,区别与分页的接口    return ResultUtil.data();}//新增用户@PostMappingpublic R add(@RequestBody User user) {    //新增用户为新资源写入,采用POST接口,入参为用户的实体    return ResultUtil.data();}//修改用户@PutMapping("{id}")public R upp(@PathVariable("id") String id,@RequestBody User user) {    //修改用户所有属性,采用PUT接口,入参为用户的实体,同时id通过path传值    return ResultUtil.data();}//删除用户@DeleteMapping("{id}")public R del(@PathVariable("id") String id) {    //删除用户,采用DELETE协议,id通过path传值    return ResultUtil.data();    }//修改用户部分属性(这里举例修改用户姓名)@PatchMapping("user/userName/{id}")public R uppPart(@PathVariable("id") String id,@PartBody String userName) {    //修改用户部分属性,采用PATCH协议,在基础路由user后面加入要修改的属性名,入参用自定义注解@PartBody,原理就是解析body里单个叫userName的值,也可用Map接收,用自定义注解只是为了后期好维护.    return ResultUtil.data();    }    
复制代码


注:切记不要直接使用@RequestMapping()注解,不准确的接口协议定义会导致url重复,客户端也可以通过任意协议调用API接口,很不规范

内容类型(content-Type)规范

application/x-www-form-urlencoded 类型

代码实例

  • application/x-www-form-urlencoded form 表单的默认传输格式,常会在后面跟上编码,即:application/x-www-form-urlencoded;charset=utf-8

  • 此传输格式时,数据会以键值对的形式传输

  • 当为 GET 请求时,浏览器用 x-www-form-urlencoded 的编码方式把 form 数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串 append 到 url 后面,用?分割,加载这个新的 url。需要对参数进行 urlencode 编码和序列化

  • 当为 POST 请求时,浏览器把 form 数据封装到 http body 中,不可用 @RequestBody 注解修饰接收实体.


multipart/form-data 类型

代码实例

  • multipart/form-data form 表单的扩展传输格式

  • 此传输格式时会把整个表单以控件为单位分割,并为每个部分加上 Content-Disposition(form-data 或者 file),Content-Type(默认为 text/plain),name(控件 name)等信息,并加上分割符(boundary)。

  • 当为 GET 请求时,入参对象或者单属性均可与传入的 name 键值对一一对应

  • 当为 POST 请求时,浏览器把 form 数据封装到 http body 中,不可用 @RequestBody 注解修饰接收实体.


application/json 类型

代码实例

  • application/json JSON 传输,现在比较推荐的传输方式

  • 此传输格式时,数据主体是序列化后的 JSON 字符串

  • 当为 GET 请求时,?传参方式可传值,参数名为实体内的属性值,用 @RequestBody 注解修饰,可直接获取到参数名对应的入参

  • 当为 POST 请求时候,body 中,不可用 @RequestBody 注解修饰接收实体


统一返回类

统一返回类是必须的.统一以后客户端就只需要一个公共解析类即可.对应的业务模型放在泛型 result 对象中,客户端就只需要用对应的解析器解析剩下的部分.

返回类 R


@Datapublic class R<T> implements Serializable    private static final long serialVersionUID = 1L;    //标识请求是否成功    private boolean success;    //操作成功或者失败后,客户端的提示信息    private String message;    //http状态码或者自定义的异常状态码    private Integer code;    //当前请求的返回时间    private long timestamp = System.currentTimeMillis();    //返回给客户端的业务主体数据,可为列表或者单个对象    private T result;}
复制代码

封装返回类工具类 ResultUtil

主要封装一些常用的成功,失败或者回参的静态方法,在控制层返回前端时,只需要返回 ResultUtil.xxx() 对应的方法即可

ResultUtil 代码实例

错误码及消息 ErrorCode

用枚举类型定义 ErrorCode,在程序异常时可直接调用 error(Integer code, String msg) 返回给客户端对应的业务异常或者其他系统异常

ErrorCode 代码实例

统一异常处理

异常拦截类 GlobalExceptionHandler

定义 GlobalExceptionHandler 类,用@ControllerAdvice修饰,可实现统一的异常拦截,代码示例中拦截了常见的一些异常类型.

//参照格式//ExceptionHandler 指定需要拦截的异常类型@ExceptionHandler(value = Exception.class)@ResponseBody//HttpServletRequest 可得到对应的请求参数,Exception 对象可得到对应的异常输出,可记录在日志中便于排查,再返回给客户端相对友好的提示public R ExceptionHandler(HttpServletRequest req, Exception e) {        log.error(String.format("Exception requestURI:%s", req.getRequestURI()), e);        return ResultUtil.error(500, "服务器内部错误");}
复制代码

GlobalExceptionHandler 代码实例

业务异常类 BusinessException

定义业务异常类是为了一些比较特殊的情况,此类继承 RuntimeException,在复杂的业务中也可定义多个业务异常类型,业务中出现一些逻辑异常就可使用这个业务异常抛出,比如字效验密码错误或者某字段数值超出临界内等等情况.可与上面的 ErrorCode 类结合使用,定义 code 应该避免一些系统预设的 http状态码

BusinessException 代码实例


后记

本文主要介绍了 API 遵循 Restful 方式的设计方案,传输内容规范以及统一返回类和统一异常拦截.涉及到的代码已经更新到 github 上 easyDemo-validation 项目中,下一期的文章会给大家分享validation验证包的使用以及接口设计上统一规范的小技巧,欢迎大家 start 持续关注.


发布于: 2020 年 12 月 23 日阅读数: 46
用户头像

陈云轩

关注

还未添加个人签名 2018.09.30 加入

还未添加个人简介

评论

发布
暂无评论
编写令人愉悦的API接口(一)