REST

一  REST的产生背景

         1. 产生背景

                    互联网做为一种新型的软件产生,使得软件和网络融合(传统上,软件和网络是两个不同的领域,很少有交集,

             软件开发主要针对单机环境,网络则主要研究系统之间的通信。),我们必须要考虑如何开发在互联网环境中使用的

             软件。于是就有了RESTful架构,它具有结构清晰、符合标准、易于理解、扩展方便的特点。

         2. 概念提出

                    REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。

                   他是这样介绍的:“本文研究计算机科学两大前沿----软件和网络----的交叉点。长期以来,软件研究主要关注软件

            设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系

            统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改

            变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网

            络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。”

二  什么是REST(Representational State Transfer,表现层状态转化)

         1. 什么是REST

                    简单的说就是就是用URL定位资源,用HTTP描述操作。

             例如:

                     增加一个朋友,uri: generalcode.cn/v1/friends 接口类型:POST

                     删除一个朋友,uri: generalcode.cn/va/friends 接口类型:DELETE

                     修改一个朋友,uri: generalcode.cn/va/friends 接口类型:PUT

                     查找朋友,uri: generalcode.cn/va/friends 接口类型:GET

             举个反例:generalcode.cn/va/deleteFriends 该接口用来表示删除朋友,这就是不符合REST协议的接口。

             在举个例子

             在Restful之前的操作:

             http://127.0.0.1/user/query/1 GET  根据用户id查询用户数据
             http://127.0.0.1/user/save POST 新增用户
             http://127.0.0.1/user/update POST 修改用户信息
             http://127.0.0.1/user/delete GET/POST 删除用户信息

             RESTful用法:

             http://127.0.0.1/user/1 GET  根据用户id查询用户数据
             http://127.0.0.1/user  POST 新增用户
             http://127.0.0.1/user  PUT 修改用户信息
             http://127.0.0.1/user  DELETE 删除用户信息

         2. 解释一下“表现出状态转化”

             REST的名称"表现层状态转化"中,其实它省略了主语。补全应该为“资源的表现层状态转化”。

           (1)资源

                           所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一种服务(比如我们开发

                    的后台接口)、一段文本、一张图片、一首歌曲、总之就是一个具体的实在。你可以用一个URI(统一资源标识

                    符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资

                    源的地址或独一无二的识别符。所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

           (2)表现层

                           "资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的“表现

                    层”。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格

                    式;图片可以用JPG格式表现,也可以用PNG格式表现。

                           URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后

                    缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信

                    息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

           (3)状态转化(State Transfer)

                            访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

                    互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想

                    要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层

                    之上的,所以就是"表现层状态转化"。  

                           客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、

                    POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更

                    新资源),PUT用来更新资源,DELETE用来删除资源。

             综合上面的解释,我们总结一下什么是RESTful架构:

             --  每一个URI代表一种资源;

             --  客户端和服务器之间,传递这种资源的某种表现层;

             --  客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"

三 RESTful设计

      1. RESTful Api设计指南

                 网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用

          设备......)。因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现

          "API First"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。我以前写过一篇《理解RESTful

          架构》,探讨如何理解这个概念。今天,我将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API。我的主要参

          考了两篇文章(1,2)

        (1)协议

                 API与用户的通信协议,总是使用HTTPs协议。

        (2)应该将API部署在专用域名之下

      https://api.example.com

 

                 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

      https://example.org/api/

 

        (3)版本号

                 应该将API的版本号放入URL

    https://api.example.com/v1/

 

                另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

        (4)路径(EndPoint)

                        在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往

                 往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用

                 复数。举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这

                 样。

 

      https://api.example.com/v1/zoos
      https://api.example.com/v1/animals
      https://api.example.com/v1/employees

 

        (5)http动词

                 常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

                 

      GET(SELECT):从服务器取出资源(一项或多项)。
      POST(CREATE):在服务器新建一个资源。
      PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
      PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
      DELETE(DELETE):从服务器删除资源。

 

                 PUT和PATCH的区别

                 假设我们有一个UserInfo,里面有userId, userName, userGender等10个字段。可你的编辑功能因为需求,在某个特

                 别的页面里只能修改userName,这时候的更新怎么做?人们通常(为徒省事)把一个包含了修改后userName的完整

                 userInfo对象传给后端,做完整更新。但仔细想想,这种做法感觉有点二,而且真心浪费带宽(纯技术上讲,你不关心带

                 宽那是你土豪)。于是patch诞生,只传一个userName到指定资源去,表示该请求是一个局部更新,后端仅更新接收到

                 的字段。而put虽然也是更新资源,但要求前端提供的一定是一个完整的资源对象,理论上说,如果你用了put,但却没

                 有提供完整的UserInfo,那么缺了的那些字段应该被清空 

                 还有两个不常用的HTTP动词。

      HEAD:获取资源的元数据。
      OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的

 

 

                 下面是一些例子:

      GET /zoos:列出所有动物园
      POST /zoos:新建一个动物园
      GET /zoos/ID:获取某个指定动物园的信息
      PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
      PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
      DELETE /zoos/ID:删除某个动物园
      GET /zoos/ID/animals:列出某个指定动物园的所有动物
      DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

 

         (6)过滤信息

                 如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。下面是一些常见的参数。

      ?limit=10:指定返回记录的数量
      ?offset=10:指定返回记录的开始位置。
      ?page=2&per_page=100:指定第几页,以及每页的记录数。
      ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
      ?animal_type_id=1:指定筛选条件

 

                 参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与

                 GET /animals?zoo_id=ID 的含义是相同的。

        (7)状态码(Status Codes)

                 服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

      200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
      201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
      202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
      204 NO CONTENT - [DELETE]:用户删除数据成功。
      400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
      401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
      403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
      404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
      406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
      410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
      422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
      500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

 

            (8) 错误处理(Error handling)

                      如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

        {
          error: "Invalid API key"
        }

 

            (9) 返回结果

                      针对不同操作,服务器向用户返回的结果应该符合以下规范

        GET /collection:返回资源对象的列表(数组)
        GET /collection/resource:返回单个资源对象
        POST /collection:返回新生成的资源对象
        PUT /collection/resource:返回完整的资源对象
        PATCH /collection/resource:返回完整的资源对象
        DELETE /collection/resource:返回一个空文档

 

            (10) Hypermedia 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"
         }

                        上面代码表示,服务器给出了提示信息,以及文档的网址。

            (10) 其他

                        API的身份认证应该使用OAuth 2.0框架。

                        服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

       2. REST Api设计误区

         (1)URI包含动词

                  因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。举例来说,某个URI是

                  /posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。如果

                  某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误

                  的URI是:

       POST /accounts/1/transfer/500/to/2

 

                  正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:

      POST /transaction HTTP/1.1
      Host: 127.0.0.1
      from=1&to=2&amount=500.00

 

         (2)在URI中加入版本号 

       http://www.example.com/app/1.0/foo
       http://www.example.com/app/1.1/foo
       http://www.example.com/app/2.0/foo

 

                  因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的

                  Accept字段中进行区分(参见Versioning REST Services):

      Accept: vnd.example-com.foo+json; version=1.0
      Accept: vnd.example-com.foo+json; version=1.1
      Accept: vnd.example-com.foo+json; version=2.0

 

       3. RESTful设计优缺点

           优点:

         (1)适合开放性高的API。这几年的由于移动互联网流行使得前端设备多样化,业界急需一种统一的机制来规范API设计,

                  使得API适用于各种各样的前端设备,REST符合这种需求。

         (2)行为和资源分离,更容易理解。

         (3)提出使用版本号(例如v1、v2),更加规范。

           缺点

         (1)对后端开发人员要求高,业务逻辑有时难以被抽象为资源的增删改查。

         (2)对前端开发人员不友好,API粒度较粗,难以查询符合特殊要求的数据,同样的业务要比普通的API需要更多次HTTP请求。

           

 

posted @ 2019-04-05 12:31  jialanshun  阅读(775)  评论(0编辑  收藏  举报