RESTfull定义

很多人都有这样的疑问:

  • 什么是RESTful API/RESTful接口?
  • RESTful接口规范是什么样子的?

下面是摘抄自百度百科词条RESTful。

REST(英文:Representational State Transfer,简称REST)描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,Roy Fielding是 HTTP 规范的主要编写者之一。在目前主流的三种Web服务交互方案中,REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-RPC更加简单明了,无论是对URL的处理还是对Payload的编码,REST都倾向于用更加简单轻量的方法设计和实现。值得注意的是REST并没有一个明确的标准,而更像是一种设计的风格。REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。RESTFUL特点包括:
1、每一个URI代表1种资源;
2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
3、通过操作资源的表现形式来操作资源;
4、资源的表现形式是XML或者HTML;
5、客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

RESTful标准用法实践

这里以一个物流系统为例定义一组接口,这里需要完成的功能如下

  • 维护物流运单。用户需要对物流运单进行增删改查操作。每个运单上都有单号,承运商,运输方式等信息。
  • 维护发票信息。用户需要管理发票,每个发票都有重量,金额和相关物流运单号。
  • 统计不同运输方式、承运商在给定时间的费用信息。

通过需求可以清晰的看到两个资源,物流运单和发票,分别定义为delivery和invoice,根据REST风格的特点,定义如下接口

  • 创建物流运单

    REST建议使用POST创建资源

    创建物流运单接口
    URL:[POST]/delivery
    Request: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
    }
    Response: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
    }
    

错误码:

  • 200: 正常创建

    • 409:单号冲突了
    • 422:参数不正确。仅靠错误码那难以给出更为准确的提示,类似:运输方式kongyun错误,有效运输方式为[Air,Sea,Truck]。
  • 查询物流运单

    REST建议使用GET来获取资源。

    查询所有:
    URL:[GET]/delivery
    Response: [ {运单信息}, {运单信息} ]根据ID查询
    URL:[GET]/delivery/{delivery-id}
    Response: {运单信息}多条件查询
    URL:[GET]/delivery/?mot={运输方式}&carrier={承运商}...
    Response: [ {运单信息}, {运单信息} ]
    

    错误码:

    • 200: 正常查询
    • 404: 查询的资源不存在
    • 422: 查询参数不正确

    在实际应用中,这个查询被挑战的最多

    • 参数太多难以传递

      GET传递参数的常规方式就是URL参数,但URL长度是受限的,难以完成类似一次查询1万单号这种需求。

    • 复杂参数难以传递

      GET传递参数的常规方式就是URL参数,但是URL上的参数只能传递结构简单的数据,难以处理对象数组参数,类似[{“carrier”=“c1”, “mot”=“Sea”}, {“carrier”=“c2”, “mot”=“Air”}]这种复杂参数需要特殊处理。对于把文件当做过滤条件的就更处理不了了。

    • 工作量多

      后台在处理复杂查询请求时,一般都会有一个表示请求条件的结构,通过URL参数构造这个结构需要额外工作量,前台在发送请求时,将所有参数拼接到URL中也需要工作量,还面临着各种编码问题。

  • 修改物流运单

    REST建议使用PUT来获取资源。

    修改物流运单接口
    URL:[PUT]/delivery/{delivery-id}
    Request: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
    }
    Response: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
    }
    

    错误码:

    • 200: 正常修改
    • 422: 参数错误。和创建一样无法报告更为准确的错误。

    在实际应用中,这个修改操作还面临着批量修改和部分修改的挑战

    • 批量修改

      用户希望修改满足条件的所有运单的某些属性为固定值。这个需求很常见,但是REST没说怎么办,当前基本上是开发人员在自由发挥。

    • 部分修改

      复杂的业务数据经常涉及到部分修改功能。比如每抵达一站,都会有一个称重员对货物称重,记录称重结果,这个人不关系目的地一类的信息,他只是在这个运单上增加一个称重记录

  • 删除物流运单

    REST建议使用DELETE来获取资源。

    删除物流运单接口
    URL:[DELETE]/delivery/{delivery-id}
    Response: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
    }
    

    实际操作还有批量删除的场景

  • 批量上传物流运单

    REST没建议

  • 批量下载物流运单

    REST没建议

  • 发票管理接口。参照物流运输单来一套就好了。

  • 统计接口

    统计信息是发票和运单的综合数据,我们称他为分摊后发票,是每个发票在单个运单上的金额信息,记为delivery-invoice。在后台可以有一个组件在自动更新。虽然这个接口是一个查询接口,但是查询的内容不是原始数据,是统计数据,不知道应该叫什么URL了。

REST enhance

技术和规范都是服务于业务的,在业务需求的推动下,所有的技术和规范必须与时俱进。针对上面提到的问题,做做了一些调整。

基本规则

  • 不使用HTTP Code返回错误信息

    HTTP Code肩负着传输层的功能,让它兼职业务逻辑,其实很为难它。所有的业务返回信息,都放在Response中更为合理。所以定义了一个通用的返回结构

    {"error": [],      // 使用字符串返回所有严重的影响业务继续执行的错信息"warning": [], // 使用字符串返回所有警告类业务信息"message": ... // 根据业务需要返回需要的信息
    }
    
  • 扩展URI指定的资源

    URI仍然用于定位资源,现在资源的范围扩展到原有资源的属性

    /delivery                                          // 表示运单资源
    /delivery/123                                   // 表示某一个具体的运单资源
    /delivery/123/weight-check      // 运单123相关的重量检测记录
    /delivery/123/weight-check/1  // 运单123上id为1的重量检测记录
    /delivery/mot                                   // 这个不是资源,不能这么写
    
  • 通过Head扩展不同的Command

    在原来的4个Method无法表达的情况下,通过Head中增加REST_Command来扩展功能

    REST_Command=BATCH_CREATE       // 表示批量创建请求
    REST_Command=SEARCH        // 表示当前操作是一个查询请求
    REST_Command=SEARCH_SUMMARY    // 表示查询摘要信息,根据业务需要返回比较少的内容,类似只返回运单基本信息,没有重量检测信息
    REST_Command=PART_UPDATE       // 部分修改。此时修改操作仅仅修改运单数据,不影响重量检测列表
    REST_Command=BATCH_UPDATE      // 批量修改。
    REST_Command=BATCH_DELETE      // 批量删除
    REST_Command=GENERATE              // 对于没有持久化,而是实时计算出来的结果,使用这个命令
    
  • Request为业务参数

    业务参数必须和业务相关才是业务参数,类似用户登录信息,用于标记查询、修改一类的命名,都不是业务信息。

Demo

  • 创建物流运单

    • 创建运单

      URL:[POST]/delivery
      Request: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
      }
      Response: {"error": [],"warning": [],"message": {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234  }
      }
      
    • 创建运单子对象

      URL:[POST]/delivery/{delivery-id}/weight-check
      Request: {"id": 1,"time": "2020-02-02 02:02:02","weight": 234
      }
      Response: {"error": [],"warning": [],"message": {"id": 1,"time": "2020-02-02 02:02:02","weight": 234}
      }
      

      子对象是必须已存与运单的信息,如果没有了运单,这个信息也没有了。运单的重量检测记录,属于子对象,但是分摊到运单的发票不是,没有运单,发票还在呀。

  • 批量创建

    创建物流运单接口
    URL:[POST]/delivery
    Head: REST_Command=BATCH_CREATE
    Request: {  // 这个request可以根据需求做一些定制"doNum": ["12345678", "1234569"],"carrier": "shunfeng","mot": "kongyun","weight": 234
    }
    Response: {"error": [],"warning": [],"message": [{"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234  }, ...]
    }
    
  • 查询物流运单

    • 查询所有

      URL:[GET]/delivery
      Head: REST_Command=SEARCH;SEARCH_SUMMARY;      // 不同的命令返回数据不同
      Response: {"error": [],"warning": [],"message": [{运单信息}, {运单信息}]
      }
      
    • 根据ID查询单个

      URL:[GET]/delivery/{delivery-id}
      Head: REST_Command=SEARCH;SEARCH_SUMMARY;      // 不同的命令返回数据不同
      Response: {"error": [],"warning": [],"message": {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234,"weightCheck": [{"id": 1,"time": "2020-02-02 02:02:02","weight": 234}]}
      }
      
    • 复杂条件查询

      URL:[POST]/delivery
      Head: REST_Command=SEARCH;SEARCH_SUMMARY;      // 不同的命令返回数据不同
      Request: {"mot": ["haiyun", "kongyun"],"carrier": ["承运商1", "承运商2"]
      }
      Response: {"error": [],"warning": [],"message": [{运单信息}, {运单信息}]
      }
      
    • 查询子对象列表

      URL:[GET]/delivery/{delivery-id}/weight-check/{check-id}
      Response: {"error": [],"warning": [],"message": [{重量检测信息}, {重量检测信息}]
      }
      
  • 修改物流运单

    • 全量修改运单

      URL:[PUT]/delivery/{delivery-id}
      Request: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
      }
      Response: {"error": [],"warning": [],"message": {运单信息}
      }
      
    • 部分修改运单

      URL:[PUT]/delivery/{delivery-id}
      Head: REST_Command=PART_UPDATE
      Request: {"doNum": "12345678","carrier": "shunfeng","mot": "kongyun","weight": 234
      }
      Response: {"error": [],"warning": [],"message": {运单信息}
      }
      
    • 修改运单子对象

      URL:[PUT]/delivery/{delivery-id}/weight-check/1
      Request: {"id": 1,"time": "2020-02-02 02:02:02","weight": 234
      }
      Response: {"error": [],"warning": [],"message": {"id": 1,"time": "2020-02-02 02:02:02","weight": 234}
      }
      
    • 批量修改运单

      URL:[PUT]/delivery/
      Head: REST_Command=BATCH_UPDATE
      Request: {  // 业务自定义请求内容"doNum": ["12345678", "12345679"]"carrier": "shunfeng","mot": "kongyun","weight": 234
      }
      Response: {"error": [],"warning": [],"message": [{运单信息}]
      }
      
  • 删除物流运单

    删除操作在删除对象不存在时直接返回,因为期望结果和当前结果一致的。

    • 删除运单

      URL:[DELETE]/delivery/{delivery-id}
      Response: {"error": [],"warning": [],"message": [{运单信息}]
      }
      
    • 删除运单子对象

      URL:[DELETE]/delivery/{delivery-id}/weight-check/{check-id}
      Response: {"error": [],"warning": [],"message": [{重量检测信息}]
      }
      
    • 批量删除

      URL:[POST]/delivery
      Head: REST_Command=BATCH_DELETE
      Request: {"mot": ["haiyun", "kongyun"],"carrier": ["承运商1", "承运商2"]
      }
      Response: {"error": [],"warning": [],"message": [{运单信息}, {运单信息}]
      }
      
  • 批量上传物流运单

    批量上传可以按照批量创建的接口做。更新REST_Command=BATCH_UPLOAD。

  • 批量下载物流运单

    批量下载可以按照批量查询的接口做。更新REST_Command=DOWNLOAD。

  • 统计接口

    统计信息是发票和运单的综合数据,我们称他为分摊后发票,是每个发票在单个运单上的金额信息,记为delivery-invoice。在后台可以有一个组件在自动更新。虽然这个接口是一个查询接口,但是查询的内容不是原始数据,是统计数据,所以命名为delivery-invoice-report。

    URL:[POST]/delivery-invoice-report
    Head: REST_Command=GENERATE
    Request: {  // 自定义查询条件"mot": ["haiyun", "kongyun"],"carrier": ["承运商1", "承运商2"]
    }
    Response: {"error": [],"warning": [],"message": {统计结果}
    }
    

总结

  • URI固定表示资源,子对象也算资源,实时计算的内容使用虚拟资源
  • HTTP Code是传输层的内容,不再参与业务逻辑
  • 使用公共的Response结构来返回各种错误信息
  • 扩展Head,增加REST_Command完成各种多样功能

RESTful的正确姿势相关推荐

  1. SpringBoot 2.0 开发案例之参数传递的正确姿势

    前言 开发这么多年,肯定还有不少小伙伴搞不清各种类型的参数是如何传递的,很多同学都是拿来即用,复制粘贴一把撸,遇到问题还是一脸懵逼. 姿势 学习参数传递的正确姿势,先说怎么做,再说为什么,本质上还是复 ...

  2. Ubuntu创建新用户的正确姿势

    作者按:因为教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<Ubuntu 创建新用户的正确姿势>原文地址.更欢迎来我的小站看更多原创内容:godbmw.com,进行&q ...

  3. io在Linux,在Linux进行IO的正确姿势

    原标题:在Linux进行IO的正确姿势 很多C/C++程序虽然在做网络编程, 但大多用别人封装好的库, 对底层不甚了解, 感觉 IO 操作不是很简单吗? 我敢说, 大多数人进行 IO 的姿势都不对, ...

  4. 互联网大厂内推求职的正确姿势?

    作者 | 码农唐磊 来源 | 程序猿石头(ID:tangleithu) 背景 每个人的职业生涯基本上都离不开"投简历找工作"这件事(什么,你家里有矿?当我没说),那拿着简历找工作正 ...

  5. Android获取设备状态栏status bar高度的正确姿势

    Android获取设备状态栏高度的正确姿势 正确代码方式: int height = 0;int resourceId = getApplicationContext().getResources() ...

  6. 开发函数计算的正确姿势——支持 ES6 语法和 webpack 压缩

    为什么80%的码农都做不了架构师?>>>    首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算 ...

  7. 开发函数计算的正确姿势 —— 爬虫

    2019独角兽企业重金招聘Python工程师标准>>> 在 <函数计算本地运行与调试 - Fun Local 基本用法> 中,我们介绍了利用 Fun Local 本地运行 ...

  8. 怎么用linux的HDD存储,Linux学习的正确姿势12:Linux存储概览

    原标题:Linux学习的正确姿势12:Linux存储概览 从工作原理区分 机械 HDD 固态 SSD SSD的优势 SSD是摒弃传统磁介质,采用电子存储介质进行数据存储和读取的一种技术,突破了传统机械 ...

  9. 自学python需要买书吗-学习Python的正确姿势—基础教学,教科书该怎么买?

    学习Python的正确姿势-基础教学,教科书该怎么买? 2019-05-07 18:52:46 23点赞 377收藏 4评论 写在前面: 四月读书季,京东图书大促销,赶上优惠的尾巴,抢购了几本心水很久 ...

最新文章

  1. 客快物流大数据项目(六十二):主题及指标开发
  2. 安全 - 堡垒机 - Jumpserver
  3. HighCharts报表 API
  4. 36.rustc编译参数.txt
  5. 《大型网站服务器容量规划》一1.1 容量规划背景
  6. PHP的stdClass
  7. python编程高手之路——函数调用
  8. android setCompoundDrawables和setCompoundDrawablesWithIntrinsicBounds区别
  9. javascript图片隐写术,感觉可以用它来干点有想法的事情
  10. 2018牛客网暑假ACM多校训练赛(第六场)I Team Rocket 线段树
  11. 怎么用python下载网易云_如何使用python批量下载网易云音乐的免费音乐
  12. USB转RS232串口应用
  13. 2021年T电梯修理免费试题及T电梯修理试题及解析
  14. 计算机公式求时间差公式,日期差计算(Excel表格中如何计算日期、时间差)
  15. Annoying day
  16. java8:新特性及Lamber表达式语法及四大函数接口
  17. oppo文件管理android在哪里,OPPO手机中缓存视频文件路径在哪里查看?怎么查看?
  18. 《大明王朝1566》台词摘录
  19. 机器学习Numpy库入门25例
  20. RealView MDK开发工具

热门文章

  1. 对话系统 | (7) 对话系统简介
  2. 如何查看网站访客信息?
  3. 硬核干货,Java工程师飞升之路
  4. geode mysql_Geode多站点(MultiSite)同步实验
  5. 快捷键切换顶视图和底视图
  6. pthread_create函数详解(向线程函数传递参数)
  7. F - Sugoroku2 (期望dp)
  8. mysql 统计时长_使用 mysql 统计平均用户在线时长
  9. 3ds  max中复制、实例和参考的区别
  10. 好的产品运营需要用好哪些工具?