继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

什么是restful?怎样用通俗的语言解释restful?

睡魔的谎言
关注TA
已关注
手记 81
粉丝 3
获赞 45

什么是RESTful

一. 什么是RESTful 

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”

  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”

  • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性

  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

二. RESTful API设计

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

  • 域名 

    • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)

    • https://example.org/api/                        API很简单

  • 版本

    • URL,如:https://api.example.com/v1/

    • 请求头                                                  跨域时,引发发送多次请求

  • 路径,视网络上任何东西都是资源,均使用名词表示(可复数)

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

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

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

  • method

    • GET      :从服务器取出资源(一项或多项)

    • POST    :在服务器新建一个资源

    • PUT      :在服务器更新资源(客户端提供改变后的完整资源)

    • PATCH  :在服务器更新资源(客户端提供改变的属性)

    • DELETE :从服务器删除资源

  • 过滤,通过在url上传参的形式传递搜索条件

    • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量

    • https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置

    • https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数

    • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序

    • https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件

  • 状态码

    http://img1.mukewang.com/5acb3c8700013dc501600160.jpg

    http://img1.mukewang.com/5acb3c8700013dc501600160.jpg

    http://img1.mukewang.com/5acb3c8700013dc501600160.jpg

    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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    
    更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

    http://img1.mukewang.com/5acb3c8700013dc501600160.jpg

    http://img1.mukewang.com/5acb3c8700013dc501600160.jpg

  • 错误处理,状态码是4xx时,应返回错误信息,error当做key。

    1

    2

    3

    {

        error: "Invalid API key"

    }

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

    1

    2

    3

    4

    5

    6

    GET /collection:返回资源对象的列表(数组)

    GET /collection/resource:返回单个资源对象

    POST /collection:返回新生成的资源对象

    PUT /collection/resource:返回完整的资源对象

    PATCH /collection/resource:返回完整的资源对象

    DELETE /collection/resource:返回一个空文档

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

    1

    2

    3

    4

    5

    6

    {"link": {

      "rel":   "collection https://www.example.com/zoos",

      "href""https://api.example.com/zoos",

      "title": "List of zoos",

      "type""application/vnd.yourformat+json"

    }}

  摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 

三. 基于Django实现

路由系统:

1

2

3

urlpatterns = [

    url(r'^users', Users.as_view()),

]

CBV视图:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

from django.views import View

from django.http import JsonResponse


class Users(View):

    def get(self, request, *args, **kwargs):

        result = {

            'status': True,

            'data': 'response data'

        }

        return JsonResponse(result, status=200)


    def post(self, request, *args, **kwargs):

        result = {

            'status': True,

            'data': 'response data'

        }

        return JsonResponse(result, status=200

四. 基于Django Rest Framework框架实现

1. 基本流程

url.py

1

2

3

4

5

6

from django.conf.urls import url, include

from web.views.s1_api import TestView


urlpatterns = [

    url(r'^test/', TestView.as_view()),

]

views.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

from rest_framework.views import APIView

from rest_framework.response import Response



class TestView(APIView):

    def dispatch(self, request, *args, **kwargs):

        """

        请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法

         

        注意:APIView中的dispatch方法有好多好多的功能

        """

        return super().dispatch(request, *args, **kwargs)


    def get(self, request, *args, **kwargs):

        return Response('GET请求,响应内容')


    def post(self, request, *args, **kwargs):

        return Response('POST请求,响应内容')


    def put(self, request, *args, **kwargs):

        return Response('PUT请求,响应内容')

上述是rest framework框架基本流程重要的功能是在APIView的dispatch中触发。

2.  认证和授权

a. 用户url传入的token认证

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 请求头认证

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. 多个认证规则

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

d. 认证和权限

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

e. 全局使用

上述操作中均是对单独视图进行特殊配置,如果想要对全局进行配置,则需要再配置文件中写入即可。

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

3. 用户访问次数/频率限制

a. 基于用户IP限制访问频率

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 基于用户IP显示访问频率(利于Django缓存)

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. view中限制请求频率

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

d. 匿名时用IP限制+登录时用Token限制

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

e. 全局使用

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings

4. 版本

a. 基于url的get传参方式

如:/users?version=v1

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 基于url的正则方式

如:/v1/users/

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. 基于 accept 请求头方式

如:Accept: application/json; version=1.0

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

d. 基于主机名方法

如:v1.example.com

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

e. 基于django路由系统的namespace

如:example.com/v1/users/

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

f. 全局使用

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

5. 解析器(parser) 

根据请求头 content-type 选择对应的解析器就请求体内容进行处理。

a. 仅处理请求头content-type为application/json的请求体

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. 仅处理请求头content-type为multipart/form-data的请求体

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg upload.html

d. 仅上传文件

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg upload.html

e. 同时多个Parser

当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

f. 全局使用

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg settings.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取

6. 序列化

序列化用于对用户请求数据进行验证和数据进行序列化。

a. 自定义字段

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 基于Model自动生成字段

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. 生成URL

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

d. 自动生成URL

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

7. 分页

a. 根据页码进行分页

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urs.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 位置和个数进行分页

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. 游标分页

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

8. 路由系统

a. 自定义路由

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 半自动路由

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. 全自动路由

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

9. 视图

a. GenericViewSet

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. ModelViewSet(自定义URL)

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. ModelViewSet(rest framework路由)

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

10. 渲染器

根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。
用户请求URL:

  • http://127.0.0.1:8000/test/?format=json

  • http://127.0.0.1:8000/test.json

用户请求头:

  • Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

a. json

访问URL:

  • http://127.0.0.1:8000/test/?format=json

  • http://127.0.0.1:8000/test.json

  • http://127.0.0.1:8000/test/ 

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

b. 表格

访问URL:

  • http://127.0.0.1:8000/test/?format=admin

  • http://127.0.0.1:8000/test.admin

  • http://127.0.0.1:8000/test/ 

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

c. Form表单

访问URL:

  • http://127.0.0.1:8000/test/?format=form

  • http://127.0.0.1:8000/test.form

  • http://127.0.0.1:8000/test/ 

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

d. 自定义显示模板

访问URL:

  • http://127.0.0.1:8000/test/?format=html

  • http://127.0.0.1:8000/test.html

  • http://127.0.0.1:8000/test/ 

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg urls.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg userdetail.html

e. 浏览器格式API+JSON

访问URL:

  • http://127.0.0.1:8000/test/?format=api

  • http://127.0.0.1:8000/test.api

  • http://127.0.0.1:8000/test/ 

http://img1.mukewang.com/5acb3c8700013dc501600160.jpg views.py

注意:如果同时多个存在时,自动根据URL后缀来选择渲染器。

面向前台

“刚才我们向前台点了一杯拿铁,这个过程可以用这段文字来描述”,说着,我在纸上写下了这段JSON,虽然她不知道什么叫JSON,但理解这段文字对于英语专业8级的她,实在再简单不过。

{
    "addOrder": {
        "orderName": "latte"
    }
}复制代码

“我们通过这段文字,告诉前台,新增一笔订单,订单是一杯拿铁咖啡”,接着,前台给我们返回这么一串回复:

{
    "orderId": "123456"
}复制代码

“订单ID?还是订单编号?”

“恩恩,就是订单编号”

“那我们就等着前台喊‘订单123456的客户可以取餐了’,然后就可以开吃了!”

“哈哈,你真聪明,不过,在这之前,假设我们有一张会员卡,我们想查询一下这张会员卡的余额,这时候,要向前台发起另一个询问”,我继续在纸上写着:

{
    "queryBalance": {
        "cardId": "886333"
    }
}复制代码

“查询卡号为886333的卡的余额?”

“真棒!接着,查询的结果返回来了”

{
    "balance": "0"
}复制代码

“切,没钱......”

“哈哈,没钱,现在我们要跟前台说,这杯咖啡不要了”,我在纸上写到:

{
    "deleteOrder": {
        "orderId": "123456"
    }
}复制代码

“哼,这就把订单取消啦?”

面向资源

“现在这家咖啡店越做越大,来喝咖啡的人越来越多,单靠前台显然是不行的,店主决定进行分工,每个资源都有专人负责,我们可以直接面向资源操作。”

"面向资源?”

“是的,比如还是下单,请求的内容不变,但是我们多了一条消息”,我在纸上画出这次的模型:

/orders

{
    "addOrder": {
        "orderName": "latte"
    }
}复制代码

“多了一个斜杠和orders?这是什么意思?”

“这个表示我们这个请求是发给哪个资源的,订单是一种资源,我们可以理解为是咖啡厅专门管理订单的人,他可以帮我们处理所有有关订单的操作,包括新增订单、修改订单、取消订单等操作”

“Soga...”

“接着还是会返回订单的编号给我们”

{
    "orderId": "123456"
}复制代码

“下面,我们还是要查询会员卡余额,这次请求的资源变成了cards”

/cards

{
    "queryBalance": {
        "cardId": "886333"
    }
}复制代码

“接下来是取消订单”

“这个我会”,说着,她抢走我手上的笔,在纸上写了起来:

/orders

{
    "deleteOrder": {
        "orderId": "123456"
    }
}复制代码

打上标签

“接下来,店主还想继续优化他的咖啡厅的服务流程,他发现负责处理订单的员工,每次都要去订单内容里面看是新增订单还是删除订单,还是其他的什么操作,十分不方便,于是规定,所有新增资源的请求,都在请求上面写上大大的‘POST’,表示这是一笔新增资源的请求”

“其他种类的请求,比如查询类的,用‘GET’表示,删除类的,用‘DELETE’表示”

“还有修改类的,修改分为两种,第一种,如果这个修改,无论发送多少次,最后一次修改后的资源,总是和第一次修改后的一样,比如将拿铁改为猫屎,那么用‘PUT’表示;第二种,如果这个修改,每次修改都会让这个资源和前一次的不一样,比如是加一杯咖啡,那么这种请求用‘PATCH’或者‘POST’表示”,一口气讲了这么多,发现她有点似懂非懂。

“来,我们再来重复上面那个过程,来一杯拿铁”,我边说边画着:

POST /orders

{
    "orderName": "latte"
}复制代码

"请求的内容简洁多啦,不用告诉店员是addOrder,看到POST就知道是新增",她听的很认真,理解的也很透彻。

"恩恩,返回的内容还是一样"

{
    "orderId": "123456"
}复制代码

“接着是查询会员卡余额,这次也简化了很多”

GET /cards

{
    "cardId": "886333"
}复制代码

“这个请求我们还可以进一步优化为这样”

GET /cards/886333复制代码

“Soga,直接把要查询的卡号写在后面了”

“没错,接着,取消订单”

DELETE /orders/123456复制代码

完美服务

“忽然有一天,有个顾客抱怨说,他买了咖啡后,不知道要怎么取消订单,咖啡厅一个店员回了一句,你不会看我们的宣传单吗,上面不写着:

DELETE /orders/{orderId}复制代码

顾客反问道,谁会去看那个啊,店员不服,又说到,你瞎了啊你......据说后面两人吵着吵着还打了起来...”

“噗,真是悲剧...”

“有了这次教训,店长决定,顾客下了单之后,不仅给他们返回订单的编号,还给顾客返回所有可以对这个订单做的操作,比如告诉用户如何删除订单。现在,我们还是发出请求,请求内容和上一次一样”

POST /orders

{
    "orderName": "latte"
}复制代码

“但是这次返回时多了些内容”

{
    "orderId": "123456",
    "link": {
        "rel": "cancel",
        "url": "/order/123456"
    }
}复制代码

“这次返回时多了一项link信息,里面包含了一个rel属性和url属性,rel是relationship的意思,这里的关系是cancel,url则告诉你如何执行这个cancel操作,接着你就可以这样子来取消订单啦”

DELETE /orders/123456复制代码

“哈哈,这服务真是贴心,以后再也不用担心店员和顾客打起来了”

“订单123456的客户可以取餐了”,伴随着咖啡厅的广播,我们吃起了下午茶,一杯拿铁,两支吸管......


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP