Django-drf架构 解析器的详解
一、解析器作用:
解析器的作用就是服务器接收客户端传过来的数据,把数据解析自己想要的数据类型的过程
简单说就是对请求体中的数据进行解析
简单分析:
- request.data将传来的Json数据解析成了
字典
的形式,我们可以使用request.data.get(‘请求参数’)来获取客户端传过来的值。我们知道原生的Django是不支付解析Json数据的,所以DRF将它完善。 - REST framework 包含许多内置的解析器类,允许接受各种媒体类型(media types)的请求。
注意:
- 在开发客户端应用时,必须确保在HTTP请求中发送数据时设置
Content-type
,若不设置,大多数客户端将默认使用application/x-www-form-urlencoded
,这并不是我们想要的,比如你使用ajax()
方法发送json
数据,应该确保包含contenType:'application/json'
设置。
二、源码流程:
1.查看APIView中的dispatch
发现,用initialize_request
方法进行变身,实例化了一个新的request对象并赋值给request 。
-
这个方法前面一部分将request对象传进了
initialize_request
方法进行二次封装,形成了一个新的request对象
:def dispatch(self, request, *args, **kwargs): # 将request 进行第二次封装,返回一个新的request对象 request = self.initialize_request(request, *args, **kwargs) self.request = request
-
实例化了一个Request类的一个对象,进行返回,注意实例化过程中穿的参数:parsers(解析器的意思):
def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) return Request(request, # 走解析 parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context)
-
实例化这个类:
class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): #可见,新的request._request就是原来的request对象,但是我们一般不用,因为后续还做了处理 self._request = request self.parsers = parsers or () # 使用这个方法就可以得到源类的request对象的GET数据 @property def query_params(self): return self._request.GET
2.在新的request类中找到了request.data
这个方法,这才是真正解析的开始:
-
Request类中的data方法,属性方法:
@property def data(self): if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data
-
_ load_data_and_files中,有一个
self._parse
方法:def _load_data_and_files(self): if not _hasattr(self, '_data'): self._data, self._files = self._parse()
-
找到这个
_parse
方法:def _parse(self): # 【拿到请求头中的数据类型】根据请求头中的数据类型,对应不同的解析器进行解析 media_type = self.content_type # 【self.parsers,选择解析器的步骤】 parser = self.negotiator.select_parser(self, self.parsers) try: # 解析的过程 parsed = parser.parse(stream, media_type, self.parser_context)
-
选择解析器的过程中有一个
self.parsers
,这就回到了实例化的时候,返回查看:Request类的init中:self.parsers = parsers or () # 此时的self是旧的,指的是自定义类的self某个对象 实例化request对象时:parsers=self.get_parsers()
-
自定义视图类的方法中,用了一个列表生成式,形成一个解析器对象的列表:
def get_parsers(self): return [parser() for parser in self.parser_classes] # 返回到【上面的 _parse方法,看select_parser做了什么】 def select_parser(self, request, parsers): for parser in parsers: if media_type_matches(parser.media_type, request.content_type): return parser
-
返回到了最初的自定义视图类的
perser_classes
中:如果自己设置了就用自己的,自己没设置就用父类的。 1,自己设置的情况可以理解。 2,自己没设置,用父类的情况(APIView): # 默认的解析器,可以在settings.py 设置全局 parser_classes = api_settings.DEFAULT_PARSER_CLASSES # 【APISettings实例化的对象】【DEFAULTS是配置文件里面的一个个键值对】 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) # 所以走了__getattr__方法 def __getattr__(self, attr): try: # 执行了此函数,得出的应该是一个字典,且拿到了执行后的attr的对应值 val = self.user_settings[attr] except KeyError: # 全局找不到,就走默认 val = self.defaults[attr] @property def user_settings(self): if not hasattr(self, '_user_settings'): self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) return self._user_settings
总结:
1.首先找自己视图类里面的:
class ParserView(APIView):
parser_classes = [JSONParser]
......逻辑......
2.找不到自己的,就去全局进行查找。
3.以上都不满足,就找默认的。
三、使用方式:
1.局部使用,可以基于类(APIView
)的视图上设置单个视图或视图集的解析器 :
from rest_framework.parsers import JSONParser, FormParser
# 创建一个CBV
class ParserView(APIView):
# 设置局部的解析器
parser_classes = [JSONParser]
"""
JSONParser: 表示只能解析 application/json 请求头
FormParser: 表示只能解析 application/x-www-form-urlencoded 请求头
"""
def post(self, request, *args, **kwargs):
"""
允许用户发送JSON格式的数据
a. content-type: application/json
b. {"name": 'fe_cow', "age": 28}
"""
"""
1. 获取用户请求
2. 获取用户请求体
3. 根据用户请求头和 parser_classes = [JSONParser, FormParser] 中支持的请求头进行比较
4. JSONParser对象中去请求体
5. request.data
"""
print(request.data)
return HttpResponse('Demo')
或FBV,@api_view
装饰器一起使用
from rest_framework.decorators import api_view
from rest_framework.decorators import parser_classes
from rest_framework.parsers import JSONParser
@api_view(['POST'])
@parser_classes((JSONParser,))
def example_view(request, format=None):
"""
A view that can accept POST requests with JSON content.
"""
return Response({'received data': request.data})
2.全局使用,可以使用 DEFAULT_PARSER_CLASSES
设置默认的全局解析器 :
# 全局使用,在settings.py中进行配置
REST_FRAMEWORK = {
"DEFAULT_PARSER_CLASSES": ["rest_framework.JSONParser", "rest_framework.FormParser"]
}
四、API参考:
1.JSONParser:
解析Json请求内容
- media_type:
application/json
2.FormParser:
- 解析HTML表单内容
request.data是一个QueryDict
字典,包含所有表单参数- 通常需要同时使用
FormParser
和MultiPartParser
,以完全支持 HTML 表单数据 - media_type:
application/x-www-form-urlencoded
3.MultParParser:
- 解析
文件上传
时multipart HTML 表单内容 request.data
是一个QueryDict
(其中包含表单参数和文件)- 通常需要同时使用
FormParser
和MultiPartParser
,以完全支持 HTML 表单数据 - media_type:
application/form-data
4.FileUploadParser:
-
解析文件上传内容
-
request.data
是一个QueryDict
(只包含一个存有文件的'file'
key) -
如果与
FileUploadParser
一起使用的视图是用filename
URL 关键字参数调用的,那么该参数将用作文件名 -
如果在没有
filename
URL 关键字参数的情况下调用,则客户端必须在Content-Disposition
HTTP header 中设置文件名。例如Content-Disposition: attachment; filename=upload.jpg
-
media_type:
*/*
-
注意:
-
FileUploadParser
用于本地客户端,可以将文件作为原始数据请求上传。对于基于 Web 的上传,或者对于具有分段上传支持的本地客户端,您应该使用MultiPartParser
解析器。 -
由于此解析器的
media_type
与任何 content type 都匹配,因此FileUploadParser
通常应该是在 API 视图上设置的唯一解析器。 -
FileUploadParser
遵循 Django 的标准FILE_UPLOAD_HANDLERS
设置和request.upload_handlers
属性。# views.py class FileUploadView(views.APIView): parser_classes = (FileUploadParser,) def put(self, request, filename, format=None): file_obj = request.data['file'] # ... # do some stuff with uploaded file # ... return Response(status=204) # urls.py urlpatterns = [ # ... url(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view()) ]
-