请求数据封装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"
}
- 路径 http://localhost:8000/api/users/?v=v1
- 如果传入版本不在允许的版本范围内drf直接返回错误
Not Found: /api/users/
[01/Apr/2024 01:17:33] "GET /api/users/?v=v4 HTTP/1.1" 404 6849
注意:
- 单独某个视图类使用版本处理器,创建完视图类后直接声明处理器即可
- 如果想所有视图类都使用版本处理器,就要在settings里设置全局版本处理起,这样就不需要在一个个视图类中声明处理器
- 如果单独使用和全局使用混合在一起,单独使用的处理器会覆盖全局默认的处理器
源码执行流程:
【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并携带相关的的版本信息用的,例如