03请求数据封装request、版本管理

请求数据封装request、版本管理

一、请求数据再封装

  • 以前我们通过django开发项目时,视图中的request是 django.core.handlers.wsgi.WSGIRequest 类的对象,其中包含了请求相关的所有数据。

  • 而在使用drf框架时,视图中的request是rest_framework.request.Request类的对象,其是又对django的request进行了一次封装,包含了除django原request对象以外,还包含其他后期会使用的其他对象。

from rest_framework.views import APIView
from rest_framework.response import Response


class UserView(APIView):
    def get(self, request, *args, **kwargs):
        # request,不再是django中的request,而是又被封装了一层,内部包含:django的request、认证、解析器等。
        return Response({"code": 1000, "data": "xxx"})

    def post(self, request, *args, **kwargs):
        return Response({"code": 1000, "data": "xxx"})
对象 = (request, 其他数据)
# rest_framework.request.Request 类
# Request类
class Request:
    """
    Wrapper allowing to enhance a standard `HttpRequest` instance.
    Kwargs:
        - request(HttpRequest). The original request instance. (django中的request)
        - parsers(list/tuple). The parsers to use for parsing the
          request content.
        - authenticators(list/tuple). The authenticators used to try
          authenticating the request's user.
    """

    def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
    		self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        ...
	
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data
    
	def __getattr__(self, attr):
        try:
            return getattr(self._request, attr) # self._request.method
        except AttributeError:
            return self.__getattribute__(attr)

在使用drf框架开发时,视图中的request对象与原来的有些不同,例如

from rest_framework.views import APIView
from rest_framework.response import Response
from django.views import View
from rest_framework.request import Request


class UserView(APIView):
    def get(self, request, *args, **kwargs):
        
        # 通过对象的嵌套直接找到原request,读取相关值
        request._request.method
        request._request.GET
        request._request.POST
        request._request.body
        
        # 举例:
        	content-type: url-form-encoded
        	v1=123&v2=456&v3=999
            django一旦读取到这个请求头之后,就会按照 {"v1":123,"v2":456,"v3":999}
            
            content-type: application/json
            {"v1":123,"v2":456}
            request._request.POST
            request._request.body
        
        # 直接读取新request对象中的值,一般此处会对原始的数据进行一些处理,方便开发者在视图中使用。
        request.query_params  # 内部本质上就是request._request.GET
        request.data # 内部读取请求体中的数据,并进行处理,例如:请求者发来JSON格式,他的内部会对json字符串进行反序列化。
        
        # 通过 __getattr__ 去访问 request._request 中的值
        request.method
        
        

二、request对象分析,源码解析Request

# 分析APIVIew时,分析出,以后request都是新的request了,是drf提供的Request的对象
	from rest_framework.request import Request
    
    
# 源码解析之 __init__--->老的request在新的内部---》request._request:
	-先看 __init__--->类实例化得到对象时,对对象进行初始化,往对象中放数据
    def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
        # 传入的request是老的,django原生的request
        # 放到了self._request,self 是新的request类的对象
        self._request = request
        self._data = Empty
        self._files = Empty
    -什么时候调用的 __init__?
    	新的                                老的
    	-request = self.initialize_request(request, *args, **kwargs)
        				  老的
        	return Request(request)
        
        
# 以后用新的跟用老的一样,为什么?
	新的  requet.method
    新的   request.path
# 魔法方法:
	在类内部,以 __开头  __结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法
    学过:__init__:  类名() 自动触发
          __str__:   print(对象)  自动触发
        
    还有哪些? 很多---》所有类都继承object类---》它都在object类中
    # 今天要学的 __getattr__ 
    	-对象.属性 ,属性不存在会触发
        
# 回头看  新的 requet.method用的时候,如果method不存在就会触发 Request类的 __getattr__
# 源码解析之 __getattr__
	-逻辑肯定是:从老的request中取出,你想要的东西
    
    def __getattr__(self, attr):
        try:
            # 通过反射,去老的中取,能取到就返回,取不到,执行except代码,再取不到就报错
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
        
        
        
# 以后新的request中多了个属性 data---》前端post,put提交的请求体中得数据,都会放在request.data中,无论何种编码格式,它都是字典




######## 总结
	-1 新的request中有老的requet, 在request._request
    -2 新的request 多了data属性,客户端提交的请求体中得数据,无论以那种方式编码,都在request.data中
    
    -3 其他的使用,跟之前老request一模一样
    	request.method
        request.path
        request.POST
        request.GET
        request.FILES
        。。。
        
# 总结:
	- 1 原生django--》post--》提交数据,只能处理urlencoded和form-data编码,从request.POST中取
    - 2 原生djagno--》put--》提交数据,处理不了,需要我们自己从body中取出来处理
    	-分不同编码格式:
        	urlencoded ---》name=lqz&age=19-->字符串切割
            json----》{"xxz":"xx","yyz":"yyy"}---》json.loads
            
   -3 原生django不能处理json提交的数据,需要自己做(put,post)
	 	json----》body中:{"xxz":"xx","yyz":"yyy"}---》json.loads
    
   -4 新的request解决了所有问题
		request.data
        

三、版本管理

在restful规范中要去,后端的API中需要体现版本。

drf框架中支持5种版本的设置。

【1】URL的GET参数传递(*)QueryParameterVersioning

  • views视图层
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
# 引入版本处理器类
from rest_framework.versioning import QueryParameterVersioning


class UserView(APIView):
  	# 此处声明版本处理器
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response({"code": 1000, "data": "xxx"})

    def post(self, request, *args, **kwargs):
        return Response({"code": 1000, "data": "xxx"})
  • settings配置文件配置drf相关配置以后编写在这里
REST_FRAMEWORK = {
  	# 以v=版本号形式传参
    "VERSION_PARAM": "v",
    # 不传版本号的话,默认为v1,DEFAULT_VERSION默认版本
    "DEFAULT_VERSION": "v1",
    # 允许的版本,版本不在其中drf会自动捕捉报错
    "ALLOWED_VERSIONS": ["v1", "v2", "v3"]	
  
  	# 设置全局版本处理器,后面跟上处理器路径
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning"
}

Not Found: /api/users/
[01/Apr/2024 01:17:33] "GET /api/users/?v=v4 HTTP/1.1" 404 6849

注意:

  • 单独某个视图类使用版本处理器,创建完视图类后直接声明处理器即可
  • 如果想所有视图类都使用版本处理器,就要在settings里设置全局版本处理起,这样就不需要在一个个视图类中声明处理器
  • 如果单独使用和全局使用混合在一起,单独使用的处理器会覆盖全局默认的处理器

源码执行流程:

image-20240401101159253

【2】URL路径传递版本参数(*)URLPathVersioning

  • views视图层
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
# 引入路径版本处理器
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning


class UserView(APIView):
		# 声明版本处理 URLPathVersioning
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response({"code": 1000, "data": "xxx"})

    def post(self, request, *args, **kwargs):
        return Response({"code": 1000, "data": "xxx"})

  • url层
from django.urls import path
from app01 import views
urlpatterns = [
  	# 此处用路径专户起可以取到路径版本参数值
    path('api/<str:version>/users/', views.UserView.as_view()),
]

  • settings
REST_FRAMEWORK = {
    "VERSION_PARAM": "version",
    # 不传版本号的话,默认为v1,DEFAULT_VERSION默认版本
    "DEFAULT_VERSION": "v1",
    # 允许的版本,版本不在其中drf会自动捕捉报错
    "ALLOWED_VERSIONS": ["v1", "v2", "v3"],

    # 设置全局版本处理起,后面跟上处理起路径
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning"
}

注意:

  • settings里的VERSION_PARAM的值一定要urls path里的路径转换器的值一样,不然取不到版本号

【3】请求头传递 AcceptHeaderVersioning

  • views视图类
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
# 引入请求头版本器AcceptHeaderVersioning
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning, AcceptHeaderVersioning


# Create your views here.


class UserView(APIView):
  	# 声明请求头版本器
    versioning_class = AcceptHeaderVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response({"code": 1000, "data": "xxx"})

    def post(self, request, *args, **kwargs):
        return Response({"code": 1000, "data": "xxx"})

  • urls层
from django.urls import path
from app01 import views
urlpatterns = [
    path('api/users/', views.UserView.as_view()),
]
  • postman中调用

反向生成URL

在每个版本处理的类中还定义了reverse方法,他是用来反向生成URL并携带相关的的版本信息用的,例如

posted @ 2024-05-15 16:48  Formerly0^0  阅读(14)  评论(0)    收藏  举报