RESTful API的设计总结

RESTful API的设计总结

在查阅了很多有关RESTful API的教程案例、设计方法、原则之后,总结成笔记:

摘要

  1. REST指 表述(表征)性状态转移

  2. 资源的URI遵循可寻址性原则,具有自描述性,形式上给人直观的关联特点;

    • 一般以一级级的名词构成URI
    • 可以使用GET/POST/PUT/DELETE 对应方法对应查询、添加、更新、删除的操作
    • API的URI是基于HTTP的名词性短语,用来表示资源
    • 服务端接收到请求后返回200状态码表示成功,客户端获取的状态码不是200时表示链路/流程上某个环节出问题
  3. RESTful API 只维护资源状态而不维护客户端状态
    对RESTful而言次请求都是全新的,只需要针对本次请求做出对应的响应,不需要记录这次请求的信息


一个简单的服务端响应格式示例:

{
    "code": -32600,
    "message": "Invalid Request",
    "data": {}
}

说明:

  1. 这样是一条典型的响应文档,可以以JSON字符串形式封装
  2. code为0表示调用成功,其他为自定义的错误码
  3. message表示在API调用失败的情况下详细的错误信息,这个信息可以选择呈现给用户看,也可为空
  4. data表示服务端返回的数据,具体格式由服务端定义,API调用错误时数据为空

详细说明

设计原则

  1. 把所有起作用的数据都列出来
  2. 将所有的数据用一种层级关系表示
  3. 版本控制,可以在URL前加上一个版本号
  4. URI规范
    1. 不用大写
    2. 用"-“代替”_"
    3. 参数列表要编码(encoding)
    4. URI中的名词用复数形式
    5. URI避免层级过深,可以使用查询参数降低层级深度
    6. URI的设计不一定直接对应数据库表,可以是某些字段的抽象或者组合,依托于实体存在,如GET /user/1/addresses address可以是 country city street等字段的组合

一些补充

  1. 安全性和幂等性

    1. 具备安全性的操作不会改变资源状态,可以理解成只读;
    2. 具备幂等性的操作将会改变资源状态,但是复数次的操作对资源状态的改变效果等价
  2. 复杂查询

    类型示例说明
    过滤条件?type=1&age=16允许一定冗余/zoos/1/zoos?id=1等效
    排序?sort=age,desc
    投影?whitelist=id,name,email
    分页?limit=10&offset=3
  3. 常用查询条件组标签化,增强复用性,降低维护成本
    GET /trades?status=closed&sort=created,desc
    可以简化成:
    GET /trades#recently-closed
    GET /trades/recently-closed

  4. 常用的三种请求体(body format)格式

    1. json字符串
      POST /v1/animal HTTP/1.1
      Host: api.example.org
      Accept: application/json
      Content-Type: application/json
      Content-Length: 24
      
      {   
       "name": "Gir",
       "animalType": "12"
      }
      
    2. 浏览器POST FORM表单格式
      POST /login HTTP/1.1
      Host: example.com
      Content-Length: 31
      Accept: text/html
      Content-Type: application/x-www-form-urlencoded
      
      username=root&password=Zion0101
      
    3. 表单有文件上传时的格式
      Content-Type: multipart/form-data; boundary=-----RANDOM_jDMUxq4Ot5
      ----------------------------625409775409345732557243
      Content-Disposition: form-data; name="file1"; filename="吃饭的艺术.txt"
      Content-Type: text/plain
      
      
      ------------------------------------
      浅谈吃饭的艺术
      当大家看到这个题目,也许会说,吃饭谁不会啊...
      ------------------------------------
      
  5. 常见的响应的资源格式
    常见常用的有:json、xml、pdf、excel等等,个人比较喜欢json,具备自描述的特性,占用的空间也相对较小

  6. 客户端请求资源时可以设置各种格式的偏好程度
    Accept:application/xml;q=0.6,application/atom+xml;q=1.0
    q表示偏好的权重值

  7. 响应不做多余的包装
    返回的数据内容应简单易操作,过于复杂的数据包装会增加前端解析数据时的开销

  8. HTTP几种方法操作后返回的数据格式

    方法URI返回内容
    GET/collection返回资源对象的列表(数组)
    GET/collection/resource返回单个资源对象
    POST/collection返回新生成的资源对象
    PUT/collection/resource返回完整的资源对象
    DELETE/collection/resource返回一个空文档
  9. json格式的约定规则

    1. 时间以时间戳的长整型/浮点表示,交由客户端自行解析
    2. 不传入null字段
    3. 分页的情况用列表(数组)封装数据
      {
      "paging":{"limit":10,"offset":0,"total":729},
      "data":[{},{},{}...]
      }
      
  10. 错误处理

    1. 发生了错误后不要给客户端返回2xx响应,客户端可能会缓存状态码表示成功的http请求
    2. 正确设置http状态码,不要自定义
    3. Response body 提供
      1. 错误的代码(日志/问题追查)
      2. 错误的描述文本(展示给用户)

    对第3点进行补充

    1. 服务器端一般用异常表示 RESTful API 的错误
    2. 可能抛出的异常有两类:业务异常和非业务异常

    业务异常由自己的业务代码抛出,表示一个用例的前置条件不满足、业务规则冲突等,比如参数校验不通过、权限校验失败。

    非业务类异常表示不在预期内的问题,通常由类库、框架抛出,或由于自己的代码逻辑错误导致,比如数据库连接失败、空指针异常、除0错误等等。


    业务类异常必须提供2种信息:
    如果抛出该类异常,HTTP 响应状态码应该设成什么,这里认为应该是400/401/403;
    异常的文本描述;


    在Controller层使用统一的异常拦截器:
    设置 HTTP 响应状态码:对业务类异常,用它指定的 HTTP code;对非业务类异常,统一500;
    Response Body 的错误码:异常类名
    Response Body 的错误描述:

    • 对业务类异常,用它指定的错误文本;

    • 对非业务类异常,线上可以统一文案如“服务器端错误,请稍后再试”,开发或测试环境中用异常的 stacktrace,服务器端提供该行为的开关。

  11. 常用的HTTP状态码及表示

状态码含义解释
200OK - [GET]服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)
201CREATED - [POST/PUT/PATCH]用户新建或修改数据成功
202Accepted - [*]表示一个请求已经进入后台排队(异步任务)
204NO CONTENT - [DELETE]用户删除数据成功
400INVALID REQUEST - [POST/PUT/PATCH]用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的
401Unauthorized - [*]表示用户没有权限(令牌、用户名、密码错误)
403Forbidden - [*]表示用户得到授权(与401错误区别),但是访问是被禁止的
404NOT FOUND - [*]用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的
406Not Acceptable - [GET]用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)
410Gone -[GET]用户请求的资源被永久删除,且不会再得到的
422Unprocesable entity - [POST/PUT/PATCH]当创建一个对象时,发生一个验证错误
500INTERNAL SERVER ERROR - [*]服务器发生错误,用户将无法判断发出的请求是否成功
  1. 其他类型资源:服务类型的资源
    除了资源简单的CRUD,服务器端经常还会提供其他服务,这些服务无法直接用上面提到的URI映射。如:
  • 按关键字搜索;
  • 计算地球上两点间的距离;
  • 批量向用户推送消息;
    可以把这些服务看成资源,计算的结果是资源的presentation,按服务属性选择合适的HTTP方法。例如:
    GET /search?q=filter?category=file  搜索
    GET /distance-calc?lats=47.480&lngs=-122.389&late=37.108&lnge=-122.448
    POST /batch-publish-msg
    [{"from":0,"to":1,"text":"abc"},{},{}...]
    
  1. 异步任务
    对耗时的异步任务,服务器端接受客户端传递的参数后,应返回创建成功的任务资源,其中包含了任务的执行状态。客户端可以轮训该任务获得最新的执行进度

    // 提交任务:
    POST /batch-publish-msg
    [{"from":0,"to":1,"text":"abc"},{},{}...]
    
    // 返回:
    {"taskId":3,"createBy":"Anonymous","status":"running"}
        
    GET /task/3
    {"taskId":3,"createBy":"Anonymous","status":"success"}
    
    

    如果任务的执行状态包括较多信息,可以把“执行状态”抽象成组合资源,客户端查询该状态资源了解任务的执行情况。

    // 提交任务:
    POST /batch-publish-msg
    [{"from":0,"to":1,"text":"abc"},{},{}...]
        
    // 返回:
    {"taskId":3,"createBy":"Anonymous"}
        
    GET /task/3/status
    {"progress":"50%","total":18,"success":8,"fail":1}
    
  2. URI失效
    随着系统发展,总有一些API失效或者迁移,对失效的API,返回404 not found 或 410 gone;对迁移的API,返回 301 重定向

  3. Hypermedia API 超媒体API
    RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么

    比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档

    {"link":{
      "rel":  "collection https://www.example.com/zoos",
      "href": "https://api.example.com/zoos",
      "title":"List of zoos",
      "type": "application/vnd.yourformat+json"
    }}
    

    文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

    Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

    {
      "current_user_url":"https://api.github.com/user",
      "authorizations_url":"https://api.github.com/authorizations",
      // ...
    }
    

    从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果

    {
      "message":"Requires authentication",
      "documentation_url":"https://developer.github.com/v3"
    }
    
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值