• Django源码分析 request.POST 与 request.body 区别


    request.POST

    request实际上是django/core/handlers/wsgi.py::WSGIRequest的实例,而WSGIRequestHttpRequest的子类

    class WSGIRequest(http.HttpRequest):
        def _get_post(self):
            if not hasattr(self, '_post'):
                self._load_post_and_files()
            return self._post
    
        def _set_post(self, post):
            self._post = post
    
        POST = property(_get_post, _set_post)        
    

    获取request.POST的时候实际上是调用了WSGIRequest._get_post()方法

    现在来看一下_load_post_and_files()方法

        def _load_post_and_files(self):
            """Populate self._post and self._files if the content-type is a form type"""
            if self.method != 'POST':
                self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
                return
            if self._read_started and not hasattr(self, '_body'):
                self._mark_post_parse_error()
                return
    
            if self.content_type == 'multipart/form-data':
                if hasattr(self, '_body'):
                    # Use already read data
                    data = BytesIO(self._body)
                else:
                    data = self
                try:
                    self._post, self._files = self.parse_file_upload(self.META, data)
                except MultiPartParserError:
                    self._mark_post_parse_error()
                    raise
            elif self.content_type == 'application/x-www-form-urlencoded':
                self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
            else:
                self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
    

    正如方法的说明所述,这个方法只会处理content-type为表单类型的请求数据

    因此当Content-Type=application/json时,我们取request.POST只会得到一个空的QueryDict

    request.body

    class HttpRequest(object):
        @property
        def body(self):
            if not hasattr(self, '_body'):
                # 调用read()之后会将此参数置为True
                if self._read_started:
                    raise RawPostDataException("You cannot access body after reading from request's data stream")
    
                # Limit the maximum request data size that will be handled in-memory.
                # 通过请求头的CONTENT_LENGTH参数得到请求的数据大小,单位为字节(byte)。但有时候获取不到,见
                # https://www.cnblogs.com/nxlhero/p/11670942.html
                if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and
                        int(self.META.get('CONTENT_LENGTH') or 0) > settings.DATA_UPLOAD_MAX_MEMORY_SIZE):
                    raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.')
    
                try:
                    self._body = self.read()
                except IOError as e:
                    six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
                # 由于request.POST和request.body都会去读self._stream,因此需要在用完之后复原
                self._stream = BytesIO(self._body)
            return self._body
        
        def read(self, *args, **kwargs):
            self._read_started = True
            try:
                # _stream将在WSGIRequest中被初始化为LimitedStream的实例
                # self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
                # 即直接从WSGI server获取数据
                return self._stream.read(*args, **kwargs)
            except IOError as e:
                six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
    

    因此,request.body取出来是字节码,需要将其编码为字符串

    Content-Type=application/json时,还需要用json.loads处理得到的json字符串

    request_params = json.loads(request.body.decode("utf-8"))
    

    统一获取请求数据

    if request.method.lower() == "post":
        # 当 Content-Type 为表单类型时,这里就可以直接获取到数据了
        request_params = request.POST
        if not request_params:
            try:
                # Content-Type=application/json 的情况
                request_params = json.loads(request.body.decode("utf-8"))
            except Exception:
                # 声明了 Content-Type=application/json,却不传递json字符串
                raise ApiException
    
  • 相关阅读:
    UVALive 3938 一道被我WA了的线段树
    批量删除Zen Cart 无图片商品
    zencart加大数据表字段长度
    CSS字体中英文名称对照表
    zencart产品批量表上传后SEO三要素状态以及特价时间修改
    Linux 文件系统
    VMware Tools 安装步骤
    IDEA安装阿里规约插件
    IDEA 中无法使用 EL 表达式
    Redis主从复制
  • 原文地址:https://www.cnblogs.com/luozx207/p/13847229.html
Copyright © 2020-2023  润新知