REST framework API 知识梳理


规范:
  域名:
    api部署在专用的域名上(跨域)
    版本号(可以在url上也可以在请求头上)
  路径:
    URL的api接口要写成名词
  method:
    CBV 这种方式就是 restful 规范提供接口的方式
    ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

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

  返回值:

    返回值要有状态码
    content_type = 'application/json'
    错误时: 也要返回状态码
  过滤:
    通过URL传递搜索条件



VirtualENV:
  # 建立 不拷贝原本的环境

virtualenv --no-site-packages venv

  # 激活

进入 venv/Scripts/ 中直接执行 activate

  # 退出

deactivate

Django REST framework 框架

  安装:

pip install djangorestframework

  APIView:
    本质:就是继承django的View

  版本:
    源码: 找到就实例化,没找到就返回空,这里class 不是复数所以只能写一个类

    if self.versioning_class is None:
      return (None, None)
    scheme = self.versioning_class()
    # 这里内部应该有个个 determine_version 方法
    return (scheme.determine_version(request, *args, **kwargs), scheme)

    源码默认提供的方法(共五个,都有示例):

# 导入来查看源码
from
rest_framework.versioning import BaseVersioning

    QueryParameterVersioning # 这是URL get方式传参默认 使用version关键字
    示例:

            # 浏览器访问使用默认关键字
            http://127.0.0.1:8000/user/?version=v1
            
            class UsersView(APIView):
                # 这里指定获取版本使用的类,必须导入QueryParameterVersioning
                versioning_class = QueryParameterVersioning

                def get(self, request, *args, **kwargs):
                    response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    # 打印获取的版本号
                    print(request.version)
                    return HttpResponse(json.dumps(response_status))

    URLPathVersioning # 这是通过 URL 传递版本号
    示例:

       # 浏览器访问地址
            http://127.0.0.1:8000/user/v100
            # urls.py文件
            url(r'^user/(?P<version>\w+)', views.UsersView.as_view())
            # 源码中还提供了这种方式限制版本号的方式
            url(r'^(?P<version>[v1|v2]+)/users/$'
            
            class UsersView(APIView):
                versioning_class = URLPathVersioning

                def get(self, request, *args, **kwargs):
                    response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    print(request.version)
                    return HttpResponse(json.dumps(response_status))

    在配置文件中配置全局使用

            REST_FRAMEWORK = {
                # "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.<使用的类>" 下面示例:
                # "VERSION_PARAM": "version"  # 默认关键字
                # "DEFAULT_VERSION": "v1"   # 默认的版本
                # "ALLOWED_VERSIONS": "v1"  # 允许的版本 多个使用列表方法 ["v1", "v2"]
                "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning"
            }

    rest_framework 默认错误显示页面 需要在 django APP中注册

        INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'app01.apps.App01Config',
            # 这里注册rest_framework
            'rest_framework'
        ]

    这里进行反向生成路由

            # urls.py 文件
            url(r'^user/(?P<version>\w+)', views.UsersView.as_view(), name="xxx"),
            # 类中代码
            class UsersView(APIView):
                versioning_class = URLPathVersioning

                def get(self, request, *args, **kwargs):
                    response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    print(request.version)
                    # 下面这条就是,需要传入request对象
                    print(request.versioning_scheme.reverse(viewname='xxx', request=request))
                    return HttpResponse(json.dumps(response_status))

 


 

  认证:
    导入查看默认的认证类:

     from rest_framework.authentication import BaseAuthentication
        默认就有两个认证类:[<class 'rest_framework.authentication.SessionAuthentication'>, 
                            <class 'rest_framework.authentication.BasicAuthentication'>]
            def get(self, request, *args, **kwargs):
                response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                # 直接打印 self.authentication_classes 查看
                print(self.authentication_classes)
                return HttpResponse(json.dumps(response_status))

    源码流程:

            initialize_request 通过 get_authenticators() 获取所有的认证类封装到request对象中
            initial 通过 self.perform_authentication(request) 执行 调用 request.user 方法执行认证
            request.user 执行 self._authenticate() 方法:
                循环所有的认证类的对象,调用对象的 authenticate(self) 方法
                如果认证失败会raise错误,认证成功返回的是元组形式(user, None)
                后续不在执行, 返回None没有报错 则后续认证器继续认证

    更改为认证未通过用户显示 Anymouns的问题:

            REST_FRAMEWORK = {
                'UNAUTHENTICATED_USER': None,  # 默认未认证用户名
                'UNAUTHENTICATED_TOKEN': None,  # 默认未认证TOKEN
            }

    自定义认证:

       TK_LIST = [
                'asdasdas',
                'asdasdasdsad'
            ]


            class CustomAuthentication(BaseAuthentication):
                def authenticate(self, request):
                    # 通过query_params来获取URL传递的参数
                    tk = request.query_params.get('tk')
                    if tk in TK_LIST:
                        print('11')
                        return ('alex', None)
                    # 这种方式从数据库取数据验证用户
                    # token_obj = models.Token.objects.filter(token=tk).first()
                    # if token_obj:
                    #     return (token_obj.user, token_obj)
                    # 如果认证失败不继续下个认证器则,直接抛出错误,否则返回None
                    raise exceptions.AuthenticationFailed("认证失败")
                    # return (None,None)


            class UsersView(APIView):
                # 设置认证器
                authentication_classes = [CustomAuthentication,]

                def get(self, request, *args, **kwargs):
                    response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    print(self.authentication_classes)
                    return HttpResponse(json.dumps(response_status))
            
            # ----------------- 认证视图操作 -------------
            class AuthPage(APIView):
                def post(self, request, *args, **kwargs):
                    ret = {'code': 1000, 'msg': None}
                    # 通过 request.data 获取用户post传递的数据
                    user = request.data.get('user')
                    pwd = request.data.get('pwd')

                    user_obj = models.UserInfo.objects.filter(user=user, pwd=pwd).first()
                    if user_obj:
                        tk = self.gen_token(user)
                        models.Token.objects.update_or_create(user=user_obj, defaults={'token': tk})
                        ret['code'] = 1001
                        ret['token'] = tk
                    else:
                        ret['msg'] = "用户名或密码错误"
                    return JsonResponse(ret)

                @staticmethod
                def gen_token(username):
                    ctime = str(time.time())
                    hash = hashlib.md5(username.encode('utf-8'))
                    hash.update(ctime.encode('utf-8'))
                    return hash.hexdigest()

    认证响应头,basic验证:
      # 加上这个会出现浏览器弹个窗口认证,需要base64解密才能看见用户名密码

      def authenticate_header(self, request):
        return 'Basic realm="api"'

 


 

  权限:
    源码流程基本同上面的认证一致.也是拿到权限类,执行其中的 has_permission(self, request, view)
    返回 True 表示有权限,返回 False 没有权限,
    接收的参数中有 request 可以点出用户的相关属性.

    代码示例:

       class CustomPermission(BasePermission):
                # 这个字段是设置默认没有权限的提示
                message = "无权限"
                # 实现 has_permission 方法
                def has_permission(self, request, view):
                    # 这里自定义权限条件
                    # 如果想使用原本的request对象, request._request
                    # request._request.method  获取请求的方法
                    return True


            class UsersView(APIView):
                # 这里使用权限类
                permission_classes = [CustomPermission,]

                def get(self, request, *args, **kwargs):
                    response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    # 打印默认使用的权限类,通过这个可以找到源码默认提供的权限类
                    print(self.permission_classes)
                    return HttpResponse(json.dumps(response_status))
                    
        注意:
            # from rest_framework.views import APIView
            如果视图类继承的是APIViews  则只会执行 has_permission 方法.
            # from rest_framework.generics import GenericAPIView
            只有在继承 GenericAPIView 时才会执行 has_object_permission 方法

  节流(访问次数限制):
    源码流程还是基本同上面一致,拿到类,然后去执行类中的allow_request 方法
    返回 True 不限制, 返回 False 则不能访问并执行wait方法,返回等待多久
    默认没有限制(没有默认的类)

    查看源码提供的方法:

# from rest_framework.throttling import BaseThrottle

    简单代码示例(根据IP限制次数):

       # settings.py 文件中 
            REST_FRAMEWORK = {
                "DEFAULT_THROTTLE_RATES": {
                    'anon': '5/m'  # 这里配置访问次数限制 每分钟5次
                }
            }
            # 这里继承的 SimpleRateThrottle
            class CustomThrottle(SimpleRateThrottle):
                scope = 'anon'  # 配置文件中的字典key关键字
                
                # 此方法,仅仅去掉了原本的 验证,未做任何修改
                def get_cache_key(self, request, view):
                    # 被去掉的内容
                    #if request.user.is_authenticated:
                    #    return None  # Only throttle unauthenticated requests.
                    return self.cache_format % {
                        'scope': self.scope,
                        'ident': self.get_ident(request)
                    }


            class UsersView(APIView):
                # 配置使用节流器
                throttle_classes = [CustomThrottle,]

                def get(self, request, *args, **kwargs):
                    response_status = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    return HttpResponse(json.dumps(response_status))

    allow_request 中的操作:

        - 获取访问频率 __init__(self)
            - 获取当前用户的唯一标识 get_cache_key(self, request, view)
            - 根据用户标识,获取所有的访问记录  self.history = self.cache.get(self.key, [])
            - 获取当前时间 self.now = self.timer() 
            - 循环比对当前访问的时间和列表的中进行比较,并pop出过期的记录
            - 计算访问次数,如果大于次数限制,执行 throttle_failure 返回False, 小于的话 执行 throttle_success 返回True
            - 如果返回 False 会继续执行wait 方法,返回还有多久能访问

    实现针对用户的节流:

            重写 allow_request中 self.key 让它直接等于一个值.删除原本的self.key
            # self.key = request.user.user  例如直接等于 用户名
            self.key = self.get_cache_key(request, view)
                if self.key is None:
                    return True

  解析器:
    根据请求头 content-type 选择对应的解析器就请求体内容进行处理。
    # 此处为详细.. 参考老师的blog
    # http://www.cnblogs.com/wupeiqi/articles/7805382.html

  序列化:
    导入包:

            from rest_framework import serializers
            from rest_framework.response import Response
            from . import models

    简单的序列化示例:

       class UserSerialize(serializers.Serializer):
                user = serializers.CharField()
                pwd = serializers.CharField()
                email = serializers.CharField()
                # 跨表显示:括号内 使用source = 


            class UsersView(APIView):

                def get(self, request, *args, **kwargs):
                    response = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    user_list = models.UserInfo.objects.all()
                    ser = UserSerialize(instance=user_list, many=True) # many表示对象是多个,单个对象写False
                    response['data'] = ser.data  # ser.data 是有序字典
                    # return HttpResponse(response)  # Django的返回方法
                    return Response(response)  # restful 的返回数据方法
                    # http://127.0.0.1:8000/user/?format=json 加上 json 会直接显示json数据

    关于 restful 自带的 Response :

            # http://127.0.0.1:8000/user/?format=json 加上 json 会直接显示json数据
            # url(r'^user\.(?P<format>\w+)', views.UsersView.as_view()), 或者修改 URL 
            # 访问的时候直接 输入: http://127.0.0.1:8000/user.json

    关于 source 跨表的一些东西:

       source 不只是跨表查询, 还可以传入当前表的字段来调取字段的一些方法,比如 get_字段名_display
            甚至可以直接获取MTM字段的值,可以直接 .all  不加 括号 源码中自动加括号,但是不能传参

    requests模块发送post请求:

       import requests

            res = requests.post(url='http://127.0.0.1:8000/user.json',
                                data={
                                    'user': 'abc',
                                    'pwd': '12312',
                                    'email': '123123@qq.com'
                                })
            print(res.text)

    序列化和数据验证:

       class UserSerialize(serializers.Serializer):
                user = serializers.CharField()
                pwd = serializers.CharField()
                email = serializers.CharField(required=False) # required 表示是否是必填字段
            
            # 自定义错误信息,error_messages = {'required':'<信息>'}
            # 自定义验证 validators = [PasswordValidator(666)] 可以写类,下面示例 

            class UsersView(APIView):

                def get(self, request, *args, **kwargs):
                    response = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    user_list = models.UserInfo.objects.all()
                    ser = UserSerialize(instance=user_list, many=True)
                    response['data'] = ser.data
                    return Response(response)

                def post(self, request, *args, **kwargs):
                    ser = UserSerialize(data=request.data)
                    if ser.is_valid():  # 判断是否是正确的数据
                        print(ser.validated_data)  # 打印出正确的数据
                    else:
                        print(ser.errors)  # 打印出错误信息
                    return Response('...')

    自定义validators验证的类:

       class PasswordValidator(object):
                # __init__不是必须的,可以在传入validators时给类加括号传入参数给 __Init__
                def __init__(self, base):
                    self.base = base

                def __call__(self, value):
                    if value != self.base:
                        message = 'This field must be %s.' % self.base
                        raise serializers.ValidationError(message)

    基于Model序列化:
      基本代码示例:

                # 非常类似 django的modelform
                class UserSerialize(serializers.ModelSerializer):
                    class Meta:
                        model = models.UserInfo
                        fields = "__all__"  # 可定制显示几个字段,必须列表形式

      反向生成URL地址:

          # urls.py 中
                url(r'^test/(?P<pk>\d+)', views.UsersView.as_view(), name='xxx'),
                # 类中需要加的参数
                class UsersView(APIView):

                    def get(self, request, *args, **kwargs):
                        response = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                        user_list = models.UserInfo.objects.all()
                        # context={'request': request} 反向生成URL使用
                        ser = UserSerialize(instance=user_list, many=True, context={'request': request})
                        response['data'] = ser.data
                        return Response(response)
        
                # 序列化类自定义字段
                class UserSerialize(serializers.ModelSerializer):
                    url = serializers.HyperlinkedIdentityField(view_name='xxx') # 反向生成url
                    class Meta:
                        model = models.UserInfo
                        fields = "__all__"

      自定义错误信息,自定义验证

         class UserSerialize(serializers.ModelSerializer):
                    url = serializers.HyperlinkedIdentityField(view_name='xxx') # 反向生成url
                    class Meta:
                        model = models.UserInfo
                        fields = "__all__"
                        # 对字段自定义
                        extra_kwargs = {
                            'user':{'min_length':6},
                            'pwd': {'validators': []}
                        }

      按深度取 FK, M2M 的信息

         class UserSerialize(serializers.ModelSerializer):
                    url = serializers.HyperlinkedIdentityField(view_name='xxx') # 反向生成url
                    class Meta:
                        model = models.UserInfo
                        fields = "__all__"
                        # 按深度取FK , M2M, 2表示深度最大为10
                        depth = 2

      基于HyperlinkedModelSerializer的全自动生成url

        # urls.py文件
            # 这里的name = '<表名>-detail'
            url(r'^test/(?P<pk>\d+)', views.UsersView.as_view(), name='userinfo-detail'),
            # 序列化类, 只要继承 HyperlinkedModelSerializer 就行了
            class UserSerialize(serializers.HyperlinkedModelSerializer):
                class Meta:
                    model = models.UserInfo
                    fields = "__all__"

  分页:
    # 此处为详细.. 参考老师的blog
    # http://www.cnblogs.com/wupeiqi/articles/7805382.html

  路由和视图:
    通过URL传参来判断取多少数据,称之为手动档

    继承 GenericAPIView (GenericAPIView是继承 APIView的)

        # 当继承后的序列化方式
            class UsersView(GenericAPIView):
                # 配置查询的类
                queryset = models.UserInfo.objects.all()
                # 配置序列化类
                serializer_class = UserSerialize

                def get(self, request, *args, **kwargs):
                    response = {'code': 200, 'error_msg': None, 'content_type': 'application/json', 'data': None}
                    # 获取查询的东西
                    user_list = self.get_queryset()
                    # 获取序列化类,传入数据
                    ser = self.get_serializer(instance=user_list, many=True)
                    return Response(ser.data)

    继承 GenericViewSet 时

       # urls.py 中必须指定当 get请求时 执行list视图函数, post同理
            urls.py 中的 as_view() 内必须传参({'get':'list','post':'add'})
            # url(r'^user/', views.UsersView.as_view({'get':'list', 'post':'add'})),
            # 其他跟 GenericAPIView 基本一致
            
        # from rest_framework.viewsets import mixins
        # 可以通过直接继承mixins中的东西,来实现增删查改等操作

    直接继承 ModelViewSet 时(半自动路由):

       # urls.py 文件:
            url(r'^user/', views.UsersView.as_view({'get':'list', 'post':'add'})),
            # 类直接这样写,直接可以实现增删查改,需要urls.py中支持
            class UsersView(ModelViewSet):
                queryset = models.UserInfo.objects.all()
                serializer_class = UserSerialize

    全自动路由实现:

       # urls.py文件中:
            from app01 import views
            from rest_framework.routers import DefaultRouter
            route = DefaultRouter()
            # 括号内第一个参数为路由前缀,第二个为 view 中的视图
            route.register('userInfo', views.UsersView)

            urlpatterns = [
                url(r'^admin/', admin.site.urls),
                url(r'^', include(route.urls)),
            ]
            # Views.py 文件中, 视图类必须继承 ModelViewSet
            class UsersView(ModelViewSet):
                queryset = models.UserInfo.objects.all()
                serializer_class = UserSerialize

  源码:

     # dispatch中
        initialize_request:
            二次封装request对象(这里通过 Request 类来封装的,加了一些东西,
            加入了解析器, 认证器, 选择器(选择解析器)等)
        initial:
            get_format_suffix  # 获取URL后缀
            perform_content_negotiation # 选择器(根据用户请求选择解析器)
            determine_version # 获取版本
            perform_authentication  # 认证处理
            check_permissions  # 权限处理
            check_throttles  # 节流(限制访问次数)
        反射:
            通过反射找到对应的视图函数,并执行

  django_restframework 请求生命周期:
    中间件,路由系统,视图(对request进行二次封装,然后版本,认证,权限,节流,执行视图方法, 最后处理响应内容)
  附加内容:

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

 

posted @ 2017-12-15 11:03  neuropathy_ldsly  阅读(221)  评论(0)    收藏  举报