• 聊聊restful和restframework


    来聊聊这个RESTful

    最近这段时间在看面试信息,很多公司的岗位要求中都有这个“掌握restful框架”。但是在我的认知里,restful好像是一种标准或者是风格啊,并不是某个具体的框架。因为自己也在这块有存疑。所以去网上寻找了一下答案。

    看Url就知道要什么
    看http method就知道干什么
    看http status code就知道结果如何

    如何理解这个REST

    首先要知道全名:Representational State Transfer
    逐字逐句的理解这三个单词:

    • Representational-------------表现层
      资源是一种信息实体,他可以有多种外在表现形式(例如文本可以是.txt.html.xml.json等多种格式)
      URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式(代表一个具体的html页面),属于"表现层"范畴,而URI(我们需要的真正的资源)应该只代表"资源"的位置。它(我们请求的资源)的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

    • state ---------状态

    • transfer----------转换
      互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

    尽管我似乎明白了这个URI和状态转换的意义和意思,但是仍然不是“人话版本”。查询了知乎之后发现覃老师(覃超)已经在他的答案中引用了一个非常棒的解释

    ivony:URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作

    接下来就按照这个人话版本进行对restful的理解。

    阮一峰api设计指南:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

    如何设计

    资源就是网络上的一个实体,或者说是网络上的一个具体信息,我们使用URI(统一资源定位符)指向他,每种资源对应一个特定的URI。所谓的上网,其实就是互联网上一系列的资源互动,调用他的URI

    HTTP动词:

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

    • POST(CREATE):在服务器新建一个资源。

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

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

    • DELETE(DELETE):从服务器删除资源。

    • HEAD:获取资源的元数据。

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

    规范样例

    • 根地址:
    https://example.org/api/v1/*
    https://api.example.com/v1/*
    
    • 版本信息:可以放在URL里面,但是也可以用HTTP的header
    /api/v1/
    
    • URL使用名词而不是动词,推荐使用复数形式:
    GET /products : will return the list of all products
    
    POST /products : will add a product to the collection
    
    GET /products/4 : will retrieve product #4PATCH/
    
    PUT /products/4 : will update product #4
    
    • 资源地址推荐使用嵌套结构
    GET /friends/10375923/profile
    
    UPDATE /profile/primaryAddress/city
    
    • 警惕返回结果的大小,设置分页和限制
    ?limit=10:指定返回记录的数量
    ?offset=10:指定返回记录的开始位置。
    ?page=2&per_page=100:指定第几页,以及每页的记录数。
    ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    ?animal_type_id=1:指定筛选条件
    
    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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    
    • 在返回结果用明确易懂的文本,而且适当加入注释
    • 关于安全:自己的接口就用https,加上一个key做一次hash放在最后即可。考虑到国情,HTTPS在无线网络里不稳定,可以使用Application Level的加密手段把整个HTTP的payload加密。有兴趣的朋友可以用手机连上电脑的共享Wi-Fi,然后用Charles监听微信的网络请求(发照片或者刷朋友圈)。

    注意事项:
    API的身份认证应该使用OAuth 2.0框架。
    服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

    认证机制的坑

    https://blog.igevin.info/posts/restful-architecture-in-general/#restful

    顺带一提的Django rest framework

    Django REST Framework是一个基于Django开发的APP,用于快速搭建REST API

    安装

    pip3 install djangorestframework
    

    使用

    注册app

    settings.py

    INSTALLED_APPS = [
        ...
        'rest_framework',
    ]
    

    注册路由

    from rest_framework import routers
    from . import views
    
    
    router = routers.DefaultRouter()
    router.register(r'users', views.UserInfoViewSet)
    
    urlpatterns = [
        url(r'^', include(router.urls)),
    ]
    

    serializers,form验证以及数据库操作

    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
    

    ViewSet视图函数

    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.
        允许用户访问查看和编辑的终端路径API
        """
        queryset = models.UserInfo.objects.all().order_by('-id')
        serializer_class = serializers.UserInfoSerializer
    

    基于CBV

    url配置:

    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()),
    ]
    

    视图函数编写:

    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)
    

    编写seiralizers

    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
    

    基于FBV

    url

    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),
    ]
    

    视图:

    
    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)
    

    编写serializers

    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
    
    

    权限控制

    settings.py配置

    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'permissi.MyPermission',
        ]
    }
    

    权限控制:

    
    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
    
  • 相关阅读:
    org.hibernate.QueryException: could not resolve property
    Eclipse支持文件UTF-8编码
    Eclipse External Tool Configration Notepad++
    WIN10 devtoolsuser
    Linux查看和注销用户(User)
    java list 去重
    监控系统对比 Ganglia vs Open-falcon vs Prometheus vs Zabbix vs Nagios vs PandoraFMS
    swagger 指定字段不显示到文档里
    mysql uuid() 相同 重复
    正则校验:微信号,qq号,邮箱
  • 原文地址:https://www.cnblogs.com/scott-lv/p/7728855.html
Copyright © 2020-2023  润新知