12

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

 3 years ago
source link: https://my.oschina.net/chenyunxuan/blog/4834608
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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();
}
//新增用户
@PostMapping
public 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对象中,客户端就只需要用对应的解析器解析剩下的部分.

@Data
public 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持续关注.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK