• Django Rest Framework


    REST framework:

        前提小知识:

      1.Django CBV:

          URL:url(r'^/index',view.StudentView.as_view())

          view: class StudentView(view):

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

                      pass

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

                      pass

                    ...

      流程:request -> 路由 -> as_view() -> view() ->dispath() (基于反射进行路由分发)

      2. [item() for item [Foo,Bar]]  # 实例化两个类的实例,放入列表中  

      3. return JsonResponse(result, status=200) 使用JsonRespone 自动json.dumps(),from django.http import JsonResponse

      4. csrf_token 中间件是在process_view中执行,1.加上装饰器@csrf_exempt 免除认证 2.装饰器@csrf_protect 添加csrf认证

      5.中间件的过程:请求-> process_request1-3 ->路由 ->process_view1-3 -> 视图 -> process_exception3-1 -> process_response3-1 ->响应

          还有一个process_template_XXX

      6.请求头有哪些:

          HOST,Cookie,eferer,Accept-Language,Accept-Encoding,User-Agent

      7.响应头:

          Allow允许的请求方式,Content-Encoding,Content-Length,Content-Type,Date,Expires,Refresh,Server,Set-Cookie,Location,Last-Modified

      8.remote_addr = request.META.get('REMOTE_ADDR')获取请求IP

      9.request.body 中的值,只有在Content-type:application/x-www-form-urlencoded 并且是post提交时data为name='mihon'&age=18这种数据类型,

          自动将request.body值转换传送给request.POST

        概述:restful代表的是一种软件架构风格,与技术无关,表征状态转移,将前后端分离,它将分布在网络中某个节点的资源通过

            URL进行标识,客户端应用通过URL来获取资源的表征,获取这些表征致使这些应用状态改变。

           

            http方法      资源操作    幂等      安全

            GET             SELSET      Y        Y

            POSE            INSERT      N        N

            PUT             UPDATE      Y        N

            DELETE          DELETE      Y        N

           

            幂等性:对同一REST接口的多次访问,得到的资源状态是相同的

            安全性:对改REST接口访问,不会使服务器端资源的状态发生改变

        retful API 设计规范:

      1.API与用户的通信协议,建议使用HTTPs(安全,需要证书但是需要钱)

      2.域名

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

          .https://example.com/api   添加api到路由上

      3.版本

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

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

      4.路径

          .https://example.com/api/v1/zoos  均使用名词表示(可以复数)

      5.请求方法

          .GET    获取资源

          .POST   新建资源

          .PUT    更新资源,完整更新

          .PATCH  更新资源,改变属性

          .DELETE 删除资源

      6.过滤

          https://example.com/api/v1/zoos?limit=10&page=2

      7.状态码

          200     ok,请求成功

          201     新建或修改数据成功

          202     表示一个请求已经进入后台排队

          204     用户数据删除成功

         

          400     用户请求错误,没有进行数据的新建或者修改

          401     表示用户没有权限

          403     表示用户没有权限,访问被禁止forbidden csrf_token

          404     表示用户请求的资源不存在

          410     用户请求的资源被永久删除

          422     当创建一个对象是,发生一个验证错误

         

          500     服务器发生错误,

      8.错误处理

          返回错误时error当作key{error:"Invalid API key"}

      9.返回结果,针对不同操作

          /collection 返回资源对象的列表

          /collection/resource 返回当个资源对象

      10.返回结果中提供详细页面的链接

          {

              1:"apple"

              detail:"https://order/apple/34"

          }

         

        基于Django实现:

            class TestView(APIView):

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

                    """

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

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

                    """

                    return super().dispatch(request, *args, **kwargs) #执行父类的dispath方法,将request进行封装

        用户认证:

            流程:需要创建一个CBV

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

                2.创建一个TestAuthentication(BaseAuthentication)的类并且继承BaseAuthentication

                3.TestAuthentication 实现两个方法 authenticate,authenticate_header(用于认证错误时)

                4.创建一个CBV类TestView(APIView)继承APIView

                5.如果没有在setting中设置全局配置,需要在CBV中添加 authentication_classes = [TestAuthentication, ]

                6.全局配置,在setting中REST_FRAMEWORK ={}添加两个键值对:

                    1.当认证通过没有返回元组值时,返回设置的值None(默认是匿名用户)

                    2.添加认证类的全路径"DEFAULT_AUTHENTICATION_CLASSES":["web.utils.TestAuthentication",]

                    

            url:urlpatterns = [

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

            view:

                from rest_framework.views import APIView

                from rest_framework.response import Response

                from rest_framework.authentication import BaseAuthentication

                from rest_framework.request import Request

                from rest_framework import exceptions

                token_list = [

                    'sfsfss123kuf3j123',

                    'asijnfowerkkf9812',

                ]

       class TestAuthentication(BaseAuthentication):

           def authenticate(self, request):

               """

               用户认证,如果验证成功后返回元组: (用户,用户Token) 返回后可以通过 request.user request.auth 调用

               :param request:

               :return:

                   None,表示跳过该验证;

                       如果跳过了所有认证,默认用户和Token和使用配置文件进行设置

                       self._authenticator = None

                       if api_settings.UNAUTHENTICATED_USER:

                           self.user = api_settings.UNAUTHENTICATED_USER()

                       else:

                           self.user = None                   

                       if api_settings.UNAUTHENTICATED_TOKEN:

                           self.auth = api_settings.UNAUTHENTICATED_TOKEN()

                       else:

                           self.auth = None

                   (user,token)表示验证通过并设置用户名和Token;

                   AuthenticationFailed异常

               """

               val = request.query_params.get('token')

               if val not in token_list:

                   raise exceptions.AuthenticationFailed("用户认证失败")

               return ('登录用户', '用户token')

           def authenticate_header(self, request):

               """

               Return a string to be used as the value of the `WWW-Authenticate`

               header in a `401 Unauthenticated` response, or `None` if the

               authentication scheme should return `403 Permission Denied` responses.

               """

               # 验证失败时,返回的响应头WWW-Authenticate对应的值

               pass

       class TestView(APIView):

           authentication_classes = [TestAuthentication, ]

           permission_classes = []

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

               print(request.user)

               print(request.auth)

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

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

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

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

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

            

       全局使用:在setting 中配置,并且认证函数和view函数分离,在view函数中就可以不需要写authentication_classes = [TestAuthentication, ]

           但是,login视图是不需要走认证的因此authentication_classes = []写一个空列表就可以。

           REST_FRAMEWORK = {

               'UNAUTHENTICATED_USER': None,

               'UNAUTHENTICATED_TOKEN': None,

               "DEFAULT_AUTHENTICATION_CLASSES": [

                   "web.utils.TestAuthentication",  # 需要写全路径,根目录为当前工程的顶级目录

               ],

               "DEFAULT_PERMISSION_CLASSES": [

                   "web.utils.TestPermission",

               ],

           }

                               

        权限设置:

            1.写一个权限类 TestPermission(BasePermission)继承BasePermission

            2.添加一个静态字段message="权限认证失败",当权限认证失败,has_permission()返回False时抛异常,值为message设置的值

            3.实现一个两个方法:

                1.has_permission()权限认证成功返回True 认证失败返回Flse

                2.has_obj_permission()视图继承GenericAPIView,并在其中使用get_object获取对象,触发单独对象权限认证

            4.在CBV中添加静态字段permission_classes = [TestPermission,]

            5.全局配置"DEFAULT_PERMISSION_CLASSES": ["web.utils.TestPermission",]

               

        节流/频率限制:

            1.添加频率限制类 LuffyAnonRateThrottle(SimpleRateThrottle)继承SimpleThrottle

            2.如果需要在setting中全局配置,在LuffyAnonRateThrottle类中添加静态字段 scope = "luffy_anon"

            3.在频率限制类中实现两个方法和一个字段:

                scope = 'luffy_anon' #在源码中rate会获取scope的值,没有就会抛出异常

                1.def get_ident(self, request):

                    """

                    认证成功时:request.user是用户对象;request.auth是token对象

                    :param request:

                    :return:

                    """

                    # return request.auth.token

                    return "user_token"  #使用user_token限制

                   

                2.def get_cache_key(self, request, view): 必须重写

                    # 用户已登录,则跳过 匿名频率限制

                    if request.user:

                        return None   #放回None 表示不做限制

                    return self.cache_format % {

                        'scope': self.scope,

                        'ident': self.get_ident(request) #如果没有实现get_ident(self, request)方法就使用继承类的IP限制

                    } #需要返回 self.cache_format   

            4.如果没有在setting中设置全局设置,需要在view中throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]

            5.自定制超过频率返回中文错误,在view中添加

                    def throttled(self, request, wait):

                        """

                        访问次数被限制时,定制错误信息

                        """

                        class Throttled(exceptions.Throttled):

                            default_detail = '请求被限制.'

                            extra_detail_singular = '请 {wait} 秒之后再重试.'

                            extra_detail_plural = '请 {wait} 秒之后再重试.'

                        raise Throttled(wait)

            6.全局setting中设置:

                REST_FRAMEWORK = {

                'DEFAULT_THROTTLE_CLASSES': [

                    'api.utils.throttles.throttles.LuffyAnonRateThrottle',  #类的路径

                    'api.utils.throttles.throttles.LuffyUserRateThrottle',

                ],

                'DEFAULT_THROTTLE_RATES': {

                    'anon': '10/day',

                    'user': '10/day',

                    'luffy_anon': '10/m',

                    'luffy_user': '20/m',          #频率设置

                },}

           

        版本:

     1.基于url eg:/users?version=v1

         .setting设置

             REST_FRAMEWORK = {

                 'DEFAULT_VERSION': 'v1',            # 默认版本

                 'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本

                 'VERSION_PARAM': 'version'          # URL中获取值的key

             }

         .view中添加字段versioning_class = QueryParameterVersioning #不能是列表,QueryParameterVersioning 是URL的类

     2.基于URL正则 eg:/v1/users/

         .setting设置

             REST_FRAMEWORK = {

                 'DEFAULT_VERSION': 'v1',            # 默认版本

                 'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本

                 'VERSION_PARAM': 'version'          # URL中获取值的key

             }

         .url路由正则:

             url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),

         .view中添加字段 versioning_class = URLPathVersioning # URLPathVersioning是URL正则的类

     3.基于accept请求头 eg: Accept: application/json; version=1.0

         .setting设置

             REST_FRAMEWORK = {

                 'DEFAULT_VERSION': 'v1',            # 默认版本

                 'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本

                 'VERSION_PARAM': 'version'          # URL中获取值的key

             }               

         .view中添加字段  versioning_class = AcceptHeaderVersioning # AcceptHeaderVersioning请求头的类

        

     4.基于主机 eg: v1.example.com

         .setting设置同上

         .view中添加字段 versioning_class = HostNameVersioning # HostNameVersioning 从主机上获取version的类

        

     5.基于Django的namespace

         .versioning_class = NamespaceVersioning

     6.全局设置:

         REST_FRAMEWORK = {

         'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", #路径

         'DEFAULT_VERSION': 'v1',

         'ALLOWED_VERSIONS': ['v1', 'v2'],

         'VERSION_PARAM': 'version' }

        

     7.使用

         class TestView(APIView):

         versioning_class = NamespaceVersioning     

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

             # 获取版本

             print(request.version)

             # 获取版本管理的类

             print(request.versioning_scheme)

             # 反向生成URL

             reverse_url = request.versioning_scheme.reverse('test', request=request)

             print(reverse_url)

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

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

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

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

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

            

        解析器(parser):

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

                parser_classes = [JSONParser, FormParser, MultiPartParser, ]

            .全局使用

                REST_FRAMEWORK = {

                    'DEFAULT_PARSER_CLASSES':[

                        'rest_framework.parsers.JSONParser'     #请求头content-type:application/json

                        'rest_framework.parsers.FormParser'     #请求头content-type:application/x-www-form-urlencoded

                        'rest_framework.parsers.MultiPartParser' #请求头content-type:multipart/form-data

                    ]}

            .使用:request.data 转换的数据被封装到request中

          

        序列化:

                   

       1. ll = serializers.HyperlinkedIdentityField(view_name='xxxx') detail使用地址链接, context={'request': request}

          

      

       from rest_framework.views import APIView

       from rest_framework.response import Response

       from rest_framework import serializers

       from .. import models

       class PasswordValidator(object):

           def __init__(self, base):

               self.base = str(base)

           def __call__(self, value):

               if value != self.base:

                   message = 'This field must be %s.' % self.base

                   raise serializers.ValidationError(message)

           def set_context(self, serializer_field):

               """

               This hook is called by the serializer instance,

               prior to the validation call being made.

               """

               # 执行验证之前调用,serializer_fields是当前字段对象

               pass

       class ModelUserSerializer(serializers.HyperlinkedModelSerializer):

           ll = serializers.HyperlinkedIdentityField(view_name='xxxx')

           tt = serializers.CharField(required=False)

           class Meta:

               model = models.UserInfo

               fields = "__all__"

               list_serializer_class = serializers.ListSerializer

               #  fields = ['user', 'pwd', 'ut']

               #depth = 2

               extra_kwargs = {

                   'user': {'min_length': 6},

                   'pwd': {'validators': [PasswordValidator(666), ]},

                   'url': {'view_name': 'xxxx'},

                   'ut': {'view_name': 'xxxx'},

               }

       class TestView(APIView):

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

               # # 序列化,将数据库查询字段序列化为字典

               data_list = models.UserInfo.objects.all()

               ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})

               # # 如果Many=True

               # # 或

               # # obj = models.UserInfo.objects.all().first()

               # # ser = UserSerializer(instance=obj, many=False)

               return Response(ser.data)

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

               # 验证,对请求发来的数据进行验证

               print(request.data)

               ser = ModelUserSerializer(data=request.data)

               if ser.is_valid():

                   print(ser.validated_data)

               else:

                   print(ser.errors)

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

                   

        分页:

       1.根据数据和页码分页

       class StandardResultsSetPagination(PageNumberPagination):

           # 默认每页显示的数据条数

           page_size = 1

           # 获取URL参数中设置的每页显示数据条数

           page_size_query_param = 'page_size'

           # 获取URL参数中传入的页码key

           page_query_param = 'page'

           # 最大支持的每页显示的数据条数

           max_page_size = 1

          

      2.根据位置和个数进行分页

      class StandardResultsSetPagination(LimitOffsetPagination):

           # 默认每页显示的数据条数

           default_limit = 10

           # URL中传入的显示数据条数的参数

           limit_query_param = 'limit'

           # URL中传入的数据位置的参数

           offset_query_param = 'offset'

           # 最大每页显得条数

           max_limit = None

          

      3.根据游标进行分页

      class StandardResultsSetPagination(CursorPagination):

           # URL传入的游标参数

           cursor_query_param = 'cursor'

           # 默认每页显示的数据条数

           page_size = 2

           # URL传入的每页显示条数的参数

           page_size_query_param = 'page_size'

           # 每页显示数据最大条数

           max_page_size = 1000

           # 根据ID从大到小排列

           ordering = "id"

          

      class UserViewSet(APIView):

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

               user_list = models.UserInfo.objects.all().order_by('-id')

               # 实例化分页对象,获取数据库中的分页数据

               paginator = StandardResultsSetPagination()

               page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)

               # 序列化对象

               serializer = UserSerializer(page_user_list, many=True)

               # 生成分页和数据

               response = paginator.get_paginated_response(serializer.data)

               return response

        渲染器:

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

     1.Json渲染器

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

         http://127.0.0.1:8000/test.json

         renderer_classes = [JSONRenderer, ]

            

     2.Adnin表格渲染器

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

         http://127.0.0.1:8000/test.admin

         renderer_classes = [AdminRenderer, ]

        

     3.From表单渲染器

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

         http://127.0.0.1:8000/test.form

         renderer_classes = [HTMLFormRenderer, ]

        

     4.自定义模板

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

         http://127.0.0.1:8000/test.html

         APIView:return Response(ser.data, template_name='user_detail.html')

    5.浏览器格式API+JSONParser

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

         http://127.0.0.1:8000/test.api

          

        路由系统:

       半自动:

           url(r'^test/(?P<pk>d+)/$', s10_generic.UserViewSet.as_view(

               {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),

              

       全自动:

           router = routers.DefaultRouter()

           router.register(r'users', s10_generic.UserViewSet)

           urlpatterns = [

               url(r'^', include(router.urls)),

           ]

  • 相关阅读:
    github绑定host
    PHP安全过滤函数
    PHP界定符 <<<EOT
    file_get_contents模拟表单(POST/GET方式提交)
    排序算法(一)冒泡排序
    MySQL的limit查询优化
    SQL Server日期函数集合
    系统查找存储过程和触发器
    C#中跳转页面有那几种方法,简述其区别
    知道sql数据库中的哪个数据表最大
  • 原文地址:https://www.cnblogs.com/mihon/p/8980947.html
Copyright © 2020-2023  润新知