zoukankan      html  css  js  c++  java
  • restful API

    1. 什么是REST

    • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
    • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
    • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
    • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

    2. REST API 

     一、协议

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

    二、域名

    应该尽量将API部署在专用域名之下。

    https://api.example.com
    

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

    https://example.org/api/
    

    三、版本(Versioning)

    应该将API的版本号放入URL。

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

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

    四、路径(Endpoint)

    路径又称"终点"(endpoint),表示API的具体网址。

    在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。

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

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

    五、HTTP动词

    对于资源的具体操作类型,由HTTP动词表示。

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

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

    还有两个不常用的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:删除某个指定动物园的指定动物
    

    六、过滤信息(Filtering)

    如果记录数量很多,服务器不可能都将它们返回给用户。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 的含义是相同的。

    七、状态码(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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    

    状态码的完全列表参见这里

    八、错误处理(Error handling)

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

    {
        error: "Invalid API key"
    }
    

    九、返回结果

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

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

    十、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"
    }
    

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

    十一、其他

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

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

      

    3. REST API 接口开发

    按照规则,不借助任何框架,根据不同的请求方式做出不同的处理,并且返回内容以及相应状态码

    url(r'^servers.html$', views.servers),
    url(r'^servers/(d+).html$', views.servers_detail),
    urls.py
    # http://127.0.0.1:8000/api/servers.html GET:   获取服务器列表
    # http://127.0.0.1:8000/api/servers.html POST:  创建服务器
    # http://127.0.0.1:8000/api/servers/1.html GET: 获取单条信息
    # http://127.0.0.1:8000/api/servers/1.html DELETE: 删除单条信息
    # http://127.0.0.1:8000/api/servers/1.html PUT: 更新
    from django.http import JsonResponse
    def servers(request):
        if request.method == 'GET':
            v = models.Server.objects.values('id','hostname')
            server_list = list(v)
            return JsonResponse(server_list,safe=False)
        elif request.method == 'POST':
            # request.POST.get('...')
            # models.Server.objects.create(。。。。、)
            return JsonResponse(status=201)
    
    def servers_detail(request,nid):
        if request.method == 'GET':
            obj = models.Server.objects.filter(id=nid).first()
            return HttpResponse('...')
        elif request.method == "DELETE":
            models.Server.objects.filter(id=nid).delete()
            return HttpResponse()
        elif request.method == 'PUT':
            request.body
            models.Server.objects.filter(id=nid).update()
    views.py

    4. 什么是 Django REST framework

    Django REST framework是一个基于Django开发的app,用于快速搭建REST API。

    安装:

    pip3 install djangorestframework

    a. 快速使用

    INSTALLED_APPS = [
        ...
        'rest_framework',
    ]
    1.注册APP
    from rest_framework import routers
    from . import views
    
    
    router = routers.DefaultRouter()
    router.register(r'users', views.UserInfoViewSet)
    
    urlpatterns = [
        url(r'^', include(router.urls)),
    ]
    2.注册路由
    from rest_framework import viewsets
    from . import models
    from . import serializers
    
    # ########### 1. 基本处理方式 ###########
    
    class UserInfoViewSet(viewsets.ModelViewSet):
        """
        API endpoint that allows users to be viewed or edited.
        """
        queryset = models.UserInfo.objects.all().order_by('-id')
        serializer_class = serializers.UserInfoSerializer
    3.编写ViewSet,视图函数
    from rest_framework import serializers
    from . import models
    
    class UserInfoSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = models.UserInfo
            # fields = ('id', 'username', 'pwd','ug') # fields = '__all__'
            exclude = ('ug',)
            depth = 1  # 0<=depth<=10
    4.编写serializers,form验证以及数据库操作

    PS:最终访问路径

    [GET]          http://127.0.0.1:8000/api/users/
    [POST]         http://127.0.0.1:8000/api/users/
    [GET]          http://127.0.0.1:8000/api/users/7/
    [PUT]          http://127.0.0.1:8000/api/users/7/
    [DELETE]       http://127.0.0.1:8000/api/users/7/

    b. 基于CBV

    from django.conf.urls import url,include
    from django.contrib import admin
    from . import views
    
    urlpatterns = [
        url(r'^users/$', views.UserList.as_view()),
        url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
    ]
    1.URL
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.parsers import JSONParser
    from . import models
    from . import serializers
    
    
    class UserList(APIView):
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all()
            serializer = serializers.MySerializer(instance=user_list, many=True)
            return Response(serializer.data)
    
        def post(self, request, *args, **kwargs):
            data = JSONParser().parse(request)
            serializer = serializers.MySerializer(data=data)
            if serializer.is_valid():
                # print(serializer.data)
                # print(serializer.errors)
                # print(serializer.validated_data)
                # 如果有instance,则执行update方法;否则,执行create
                serializer.save()
                return Response(serializer.data, status=201)
            return Response(serializer.errors, status=400)
    
    
    class UserDetail(APIView):
        def get(self, request, *args, **kwargs):
            obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
            serializer = serializers.MySerializer(obj)
            return Response(serializer.data)
    
        def delete(self, request, *args, **kwargs):
            obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
            obj.delete()
            return Response(status=204)
    
        def put(self, request, *args, **kwargs):
            data = JSONParser().parse(request)
            obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
            serializer = serializers.MySerializer(obj, data=data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            return Response(serializer.errors, status=400)
    2.编写视图函数
    from rest_framework import serializers
    from rest_framework.exceptions import ValidationError
    from . import models
    
    
    class MySerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        username = serializers.CharField(required=False, allow_blank=True, max_length=100)
        pwd = serializers.CharField()
    
        def validate_username(self, value):
            if value == '中国':
                raise ValidationError('用户名中存在敏感字符')
            return value
    
        def validate_pwd(self, value):
            print(value)
            return value
    
        def validate(self, attrs):
            print(attrs)
            return attrs
    
        def create(self, validated_data):
            """
            当执行save方法时,自动调用。instance未传值
            :param validated_data:
            :return:
            """
            print(validated_data)
            return models.UserInfo.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            """
            当执行save方法时,自动调用。instance传值
            :param instance:
            :param validated_data:
            :return:
            """
            instance.username = validated_data.get('username', instance.username)
            instance.save()
            return instance
    3.编写serializers

    c. 基于CBV

    from django.conf.urls import url,include
    from django.contrib import admin
    from . import views
    
    urlpatterns = [
        url(r'^users/$', views.user_list),
        url(r'^users/(?P<pk>[0-9]+)/$', views.user_detail),
    ]
    1.URL
    from django.http import JsonResponse,HttpResponse
    from rest_framework.response import Response
    from rest_framework.parsers import JSONParser
    from rest_framework.decorators import api_view
    from .serializers import MySerializer
    from . import models
    
    @api_view(['GET',"POST"])
    def user_list(request):
        """
        List all code snippets, or create a new snippet.
        """
        if request.method == 'GET':
            user_list = models.UserInfo.objects.all()
            serializer = MySerializer(user_list,many=True)
            return Response(serializer.data)
    
        elif request.method == 'POST':
            data = JSONParser().parse(request)
            serializer = MySerializer(data=data)
            if serializer.is_valid():
                print(serializer.data)
                print(serializer.errors)
                print(serializer.validated_data)
                # 如果有instance,则执行update方法;否则,执行create
                serializer.save()
                return Response(serializer.data, status=201)
            return Response(serializer.errors, status=400)
    
    @api_view(['GET',"POST","PUT"])
    def user_detail(request, pk):
        """
        Retrieve, update or delete a code snippet.
        """
    
        obj = models.UserInfo.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse(status=404)
    
        if request.method == 'GET':
            serializer = MySerializer(obj)
            # return JsonResponse(serializer.data,json_dumps_params={'ensure_ascii':False},content_type='application/json;charset=utf-8')
            return Response(serializer.data)
    
        elif request.method == 'PUT':
            data = JSONParser().parse(request)
            serializer = MySerializer(obj, data=data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            return Response(serializer.errors, status=400)
    
        elif request.method == 'DELETE':
            obj.delete()
            return Response(status=204)
    2.视图函数
    from rest_framework import serializers
    from rest_framework.exceptions import ValidationError
    from . import models
    
    
    class MySerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        username = serializers.CharField(required=False, allow_blank=True, max_length=100)
        pwd = serializers.CharField()
    
        def validate_username(self, value):
            if value == '中国':
                raise ValidationError('用户名中存在敏感字符')
            return value
    
        def validate_pwd(self, value):
            print(value)
            return value
    
        def validate(self, attrs):
            print(attrs)
            return attrs
    
        def create(self, validated_data):
            """
            当执行save方法时,自动调用。instance未传值
            :param validated_data:
            :return:
            """
            print(validated_data)
            return models.UserInfo.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            """
            当执行save方法时,自动调用。instance传值
            :param instance:
            :param validated_data:
            :return:
            """
            instance.username = validated_data.get('username', instance.username)
            instance.save()
            return instance
    3.编写serializers

    d. 权限控制

    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'permissi.MyPermission',
        ]
    }
    1.settings配置文件
    class MyPermission(object):
        """
        A base class from which all permission classes should inherit.
        """
    
        def has_permission(self, request, view):
            """
            Return `True` if permission is granted, `False` otherwise.
            """
    
            return True
    
        def has_object_permission(self, request, view, obj):
            return True
    2.权限控制
     
     
  • 相关阅读:
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    206. Reverse Linked List
    205. Isomorphic Strings
    204. Count Primes
    203. Remove Linked List Elements
    201. Bitwise AND of Numbers Range
    199. Binary Tree Right Side View
    ArcGIS API for JavaScript 4.2学习笔记[8] 2D与3D视图同步
  • 原文地址:https://www.cnblogs.com/xuyaping/p/7308359.html
Copyright © 2011-2022 走看看