drf第四讲——请求的封装、版本、认证、权限

本文深入探讨RESTful API设计原则,包括使用HTTPS、URL版本控制及资源导向设计。同时,介绍了Django REST framework(DRF)如何简化API开发流程,涵盖序列化、分页、过滤和视图等功能。
摘要由CSDN通过智能技术生成

回顾和补充

1.restful规范

1. 建议用https代替http

2. 在URL中体现api,添加api标识
        https://www.cnblogs.com/xwgblog/p/11812244.html   # 错误
        https://www.cnblogs.com/api/xwgblog/p/11812244.html  # 正确(根域名)
        https://api.cnblogs.com/xwgblog/p/11812244.html # 正确(子域方式)

        建议使用:https://www.cnblogs.com/api/...
        
3. 在URL中要体现版本(版本可以体在在多个地方,一般就体现在url中)
	https://www.cnblogs.com/api/v1/userinfo/
	https://www.cnblogs.com/api/v2/userinfo/
	
4. URL中一般用名词: 
	http://www.luffycity.com/api/v1/article/ (面向资源编程,网络上东西都视为资源)

5. 筛选条件,在URL参数中进行传递:
	http://www.luffycity.com/article/page=1&category=1
	
6. 根据请求不同做不同操作:GET/POST/PUT/DELTE/PATCH

7. 一般传输的数据格式都是JSON

2.drf组件为我们提供了哪些功能

首先,drf是dajngo框架为我们提供的一个组件,它的本质是一个app,它可以依据restful规范快速开发为我们开发一套应用程序。
然后,他为我么做了这些:用户发来请求,请求走我们的url,在进入视图先执行dispacth,这里我们进行了版本控制、用户认证以及权限认证和频率限制。然后,drf为我们提供了解析器、序列化、分页、筛选、视图、渲染器的一些功能。其中,我们常用的是序列化、分页、筛选和视图的功能,其他的我们只需要一次配置就可以了。

3.类继承关系(各个组件之间的继承关系)


class View(object):
	def dipatch(self):
		print(123)
	
class APIView(View):
    """
    该模块是都是一些公共的功能模块
    """
    #这里定义的都是一些变量,这里变量都是链接到settings里面的,我们的功能配置参数搜可以来这里查
    version_class = settings.xxx 
	parser_class = settings.sxx
	permision_classes = []
    
	def dipatch(self):

        #在执行么的反射之前先执行这个方法
        self.initial()
        
        """和我们之前的View一样,在执行class里面的函数之前,先执行该函数,在这里面根据我们的请求通过反射找方法并执行
        """
		method = getattr(self,"get")
		return method()
    
    def initial(self):
        #这个方法里面其实执行的就是版本控制和认证和权限的功能
		self.version_class()
		self.parser_class()
		for item in self.permision_classes:
		 	item()
	
class GenericAPIView(APIView):
    """
    这个class里面实现的是一些方法和放置一些默认的变量
    它也是一个放置公共模块和变量的类,但是它提供给的都是
    和增删改查相关的类,我们下面的增删改查的类都会继承该类
    """
	queryset = None
	serilizer_class = None 
	def get_queryset(self):
		return self.queryset
		
	def get_serilizer(self,*arg,**kwargs):
		cls = self.get_serilizer_class()
		return cls(*arg,**kwargs)
	
	def get_serilizer_class(self):
		return self.serilizer_class
    
class ListModelMixin(object):
    """
    这个是我们的和增删改查相关的功能类,我们的下面的每一个功能类
    都会继承每一个对应的该类,通过他们去找 最下面的子类里面的变量或者GenericAPIView变量和
    最下面的子类里面的方法或者GenericAPIVie里面的方法,然后返回数据给请求者
    """
	def list(self):
		queryset = self.get_queryset()
		ser = self.get_serilizer(queryset,many=True)
		return Reponse(ser.data)
		
class ListAPIView(ListModelMixin,GenericAPIView):
    """
    我们的功能类,每一个类对应一个增删查改的功能,这就是个入口,通过它去执行
    xxxxModelMixin里面的方法
    """
	def get(self):
		return self.list(...)
	
	
class TagView(ListAPIView):
    """
    我们的实例,在这里我们配置相关的变量就可以执行一系列的操作,我们也可以通过重构父类方法
    来自定制我们需要的功能,(记好:父类中所有的查找都是重这个类开始的)
    """
	queryset = models.User.object.all()
	serilizer_class = TagSerilizer
	
obj = TagView()
x = obj.dispatch()

请求的封装

class HttpRequest(object):
	def __init__(self):
		pass
	
	@propery
	def GET(self):
		pass
	
	@propery
	def POST(self):
		pass
	
	@propery
	def body(self):
		pass

class Request(object):
	def __init__(self,request):
		self._request = request
	
	def data(self):
        """
        封装了request.POST=data
        """
		if content-type == "application/json"
			reutrn json.loads(self._request.body.decode('urf-8'))
		elif content-type == "x-www-...":
			return self._request.POST
		
	def query_params(self):
        """
        封装了request.GET=query_params
        """
		return self._reqeust.GET

视图

class View(object):
	@classonlymethod
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            return self.dispatch(request, *args, **kwargs)
        return view
	
class APIView(View):
	
	@classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        return csrf_exempt(view)
    
	def dispatch(self, request, *args, **kwargs):
        # 新request内部包含老request(_reuqest=老request)
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        
        self.initial(request, *args, **kwargs)
        
        # 通过反射执行“get”方法,并传入新的request
        handler = getattr(self, request.method.lower())
        response = handler(request, *args, **kwargs) # get(requst)
        return self.response
    
class OrderView(APIView):
	
    def get(self,request,*args,**kwargs):
        return Response('成功')

版本

源码
class APIView(View):
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
	def dispatch(self, request, *args, **kwargs):
       
        # ###################### 第一步 ###########################
        """
        request,是django的request,它的内部有:request.GET/request.POST/request.method
        args,kwargs是在路由中匹配到的参数,如:
            url(r'^order/(\d+)/(?P<version>\w+)/$', views.OrderView.as_view()),
            http://www.xxx.com/order/1/v2/
        """
        self.args = args
        self.kwargs = kwargs

        """
        request = 生成了一个新的request对象,此对象的内部封装了一些值。
        request = Request(request)
            - 内部封装了 _request = 老的request
        """
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request

        self.headers = self.default_response_headers  # deprecate?

        try:
            # ###################### 第二步 ###########################
            self.initial(request, *args, **kwargs)

            执行视图函数。。
	
	def initial(self, request, *args, **kwargs):
       
        # ############### 2.1 处理drf的版本 ##############
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
		...
        
    def determine_version(self, request, *args, **kwargs):
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class() # obj = XXXXXXXXXXXX()
        return (scheme.determine_version(request, *args, **kwargs), scheme)
        
class OrderView(APIView):
    versioning_class = URLPathVersioning
    def get(self,request,*args,**kwargs):
        print(request.version)
        print(request.versioning_scheme)
        return Response('...')

    def post(self,request,*args,**kwargs):
        return Response('post')
class URLPathVersioning(BaseVersioning):
    """
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
       
    ]
    """
    invalid_version_message = _('Invalid version in URL path.')

    def determine_version(self, request, *args, **kwargs):
        version = kwargs.get(self.version_param, self.default_version)
        if version is None:
            version = self.default_version

        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version

使用(局部)
  • url中写version

    url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
    
  • 在视图中应用

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.versioning import URLPathVersioning
    
    
    class OrderView(APIView):
    	#在需要的版本类里面定义该变量
        versioning_class = URLPathVersioning
        
        def get(self,request,*args,**kwargs):
            print(request.version)
            print(request.versioning_scheme)
            return Response('...')
    
        def post(self,request,*args,**kwargs):
            return Response('post')
    
  • 在settings中配置

    REST_FRAMEWORK = {
        "PAGE_SIZE":2,
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
        #配置项,设置版本
        "ALLOWED_VERSIONS":['v1','v2'],
        'VERSION_PARAM':'version'
    }
    
使用(全局)推荐
  • url中写version

    url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
    
    url(r'^(?P<version>\w+)/users/$', users_list, name='users-list'),
    
  • 在视图中应用

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.versioning import URLPathVersioning
    
    
    class OrderView(APIView):
        def get(self,request,*args,**kwargs):
            print(request.version)
            print(request.versioning_scheme)
            return Response('...')
    
        def post(self,request,*args,**kwargs):
            return Response('post')
    
  • 在settings中配置

    REST_FRAMEWORK = {
        "PAGE_SIZE":2,
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
       
     #配置项,应用全局   "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        "ALLOWED_VERSIONS":['v1','v2'],
        'VERSION_PARAM':'version'
    }
    

认证(***)

源码
class Request:

    def __init__(self, request,authenticators=None):
        self._request = request
        self.authenticators = authenticators or ()
        
	@property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
    
    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()
        
	@user.setter
    def user(self, value):
        """
        Sets the user on the current request. This is necessary to maintain
        compatibility with django.contrib.auth where the user property is
        set in the login and logout functions.

        Note that we also set the user on Django's underlying `HttpRequest`
        instance, ensuring that it is available to any middleware in the stack.
        """
        self._user = value
        self._request.user = value

使用继承


class APIView(View):
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    
	def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        # ###################### 第一步 ###########################
        """
        request,是django的request,它的内部有:request.GET/request.POST/request.method
        args,kwargs是在路由中匹配到的参数,如:
            url(r'^order/(\d+)/(?P<version>\w+)/$', views.OrderView.as_view()),
            http://www.xxx.com/order/1/v2/
        """
        self.args = args
        self.kwargs = kwargs


        """
        request = 生成了一个新的request对象,此对象的内部封装了一些值。
        request = Request(request)
            - 内部封装了 _request = 老的request
            - 内部封装了 authenticators = [MyAuthentication(), ]
        """
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
	
	def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(), # [MyAuthentication(),]
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [ auth() for auth in self.authentication_classes ]
    
class LoginView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登录失败')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

class OrderView(APIView):
    # authentication_classes = [TokenAuthentication, ]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        if request.user:
            return Response('order')
        return Response('滚')

class UserView(APIView):
    同上
使用

定义认证方法

class MyAuthentication:
    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        token = request.query_params.get('token')
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return (user_object,token)
        return (None,None)

视图

class LoginView(APIView):

    def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登录失败')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

全局使用

"DEFAULT_AUTHENTICATION_CLASSES":["api.auth.authentications.MyAuthentication",],

局部使用

class LoginView(APIView):
	#在使用的类里面设置空列表
	"DEFAULT_AUTHENTICATION_CLASSES" = []

    def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登录失败')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

权限

源码
class APIView(View):
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    
    def dispatch(self, request, *args, **kwargs):
        封装request对象
        self.initial(request, *args, **kwargs)
        通过反射执行视图中的方法

	def initial(self, request, *args, **kwargs):
        版本的处理
        # 认证
        self.perform_authentication(request)
		
        # 权限判断
        self.check_permissions(request)
        
        self.check_throttles(request)
        
    def perform_authentication(self, request):
        request.user
	
    def check_permissions(self, request):
        # [对象,对象,]
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(request, message=getattr(permission, 'message', None))
    def permission_denied(self, request, message=None):
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise exceptions.PermissionDenied(detail=message)
        
    def get_permissions(self):
        return [permission() for permission in self.permission_classes]
    
class UserView(APIView):
    permission_classes = [MyPermission, ]
    
    def get(self,request,*args,**kwargs):
        return Response('user')
使用
from rest_framework.permissions import BasePermission
from rest_framework import exceptions

class MyPermission(BasePermission):
    #自定义错误信息
    message = {'code': 10001, 'error': '你没权限'}
    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        if request.user:
            return True

        # raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'})
        return False

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
class OrderView(APIView):
    #配置变量
    permission_classes = [MyPermission,]
    def get(self,request,*args,**kwargs):
        return Response('order')

class UserView(APIView):
    permission_classes = [MyPermission, ]
    def get(self,request,*args,**kwargs):
        return Response('user')

REST_FRAMEWORK = {
    "PAGE_SIZE":2,
    "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    "ALLOWED_VERSIONS":['v1','v2'],
    'VERSION_PARAM':'version',
    #权限配置
    "DEFAULT_AUTHENTICATION_CLASSES":["kka.auth.TokenAuthentication",]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值