python(十五):Django之HttpRequest

匿名 (未验证) 提交于 2019-12-02 22:11:45

  当一个请求连接进来时,django会创建一个HttpRequest对象来封装和保存所有请求相关的信息,并且会根据请求路由载入匹配的视图函数。每个请求的视图函数都会返回一个HttpResponse。

  HttpRequest和HttpResponse可以从django.http中导入。

  1、HttpRequest类

HttpRequest.scheme 请求协议(http或者https)
HttpRequest.body 以字节的方式返回请求体内容;可以通过HttpRequest.POST获取处理后的key和value,也可以通过HttpRequest.read()格式化
HttpRequest.path 返回请求的完整路径,不包括协议和域名
HttpRequest.GET GET请求参数,返回一个queryDict对象
HttpRequest.POST 获取表单提交的数据,如果是通过POST请求提交的其它非表单数据,可以使用HttpRequest.Body获取;使用时可以通过if request.method == "PSOT"来进行预判断
HttpRequest.method 返回请求方式
HttpRequest.environ 返回一个字典,包含所有django运行的环境信息
HttpRequest.content_type 文件格式
HttpRequest.content_params 参数
HttpRequest.COOKIES 返回一个字典,包含浏览器存储的所有cookie
HttpRequest.FILES 返回一个MultiValueDict,包含上传的文件
HttpRequest.META 返回一个包含所有请求相关信息的字典(包含Headers),同environ
HttpRequest.resolver_match 返回请求处理的url及相关参数
HttpRequest.session 中间件,设置session,一个可读可写的字典对象
HttpRequest.get_host() 获取请求的主机和端口
HttpRequest.get_port() 获取端口
HttpRequest.get_full_path() 返回完整路径,同path
HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None 获取以一个cookie
HttpRequest.is_ajax() 判断是否为ajax请求
HttpRequest.is_secure() 判断是否为https请求

  示例:

class HttpRequest:     """A basic HTTP request."""      # The encoding used in GET/POST dicts. None means use default setting.     _encoding = None     _upload_handlers = []      def __init__(self):         # WARNING: The `WSGIRequest` subclass doesn't call `super`.         # Any variable assignment made here should also happen in         # `WSGIRequest.__init__()`.          self.GET = QueryDict(mutable=True)         self.POST = QueryDict(mutable=True)         self.COOKIES = {}         self.META = {}         self.FILES = MultiValueDict()          self.path = ''         self.path_info = ''         self.method = None         self.resolver_match = None         self._post_parse_error = False         self.content_type = None         self.content_params = None      def __repr__(self):         if self.method is None or not self.get_full_path():             return '<%s>' % self.__class__.__name__         return '<%s: %s %r>' % (self.__class__.__name__, self.method, self.get_full_path())      def _get_raw_host(self):         """         Return the HTTP host using the environment or request headers. Skip         allowed hosts protection, so may return an insecure host.         """         # We try three options, in order of decreasing preference.         if settings.USE_X_FORWARDED_HOST and (                 'HTTP_X_FORWARDED_HOST' in self.META):             host = self.META['HTTP_X_FORWARDED_HOST']         elif 'HTTP_HOST' in self.META:             host = self.META['HTTP_HOST']         else:             # Reconstruct the host using the algorithm from PEP 333.             host = self.META['SERVER_NAME']             server_port = self.get_port()             if server_port != ('443' if self.is_secure() else '80'):                 host = '%s:%s' % (host, server_port)         return host      def get_host(self):         """Return the HTTP host using the environment or request headers."""         host = self._get_raw_host()          # Allow variants of localhost if ALLOWED_HOSTS is empty and DEBUG=True.         allowed_hosts = settings.ALLOWED_HOSTS         if settings.DEBUG and not allowed_hosts:             allowed_hosts = ['localhost', '127.0.0.1', '[::1]']          domain, port = split_domain_port(host)         if domain and validate_host(domain, allowed_hosts):             return host         else:             msg = "Invalid HTTP_HOST header: %r." % host             if domain:                 msg += " You may need to add %r to ALLOWED_HOSTS." % domain             else:                 msg += " The domain name provided is not valid according to RFC 1034/1035."             raise DisallowedHost(msg)      def get_port(self):         """Return the port number for the request as a string."""         if settings.USE_X_FORWARDED_PORT and 'HTTP_X_FORWARDED_PORT' in self.META:             port = self.META['HTTP_X_FORWARDED_PORT']         else:             port = self.META['SERVER_PORT']         return str(port)      def get_full_path(self, force_append_slash=False):         # RFC 3986 requires query string arguments to be in the ASCII range.         # Rather than crash if this doesn't happen, we encode defensively.         return '%s%s%s' % (             escape_uri_path(self.path),             '/' if force_append_slash and not self.path.endswith('/') else '',             ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) if self.META.get('QUERY_STRING', '') else ''         )      def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None):         """         Attempt to return a signed cookie. If the signature fails or the         cookie has expired, raise an exception, unless the `default` argument         is provided,  in which case return that value.         """         try:             cookie_value = self.COOKIES[key]         except KeyError:             if default is not RAISE_ERROR:                 return default             else:                 raise         try:             value = signing.get_cookie_signer(salt=key + salt).unsign(                 cookie_value, max_age=max_age)         except signing.BadSignature:             if default is not RAISE_ERROR:                 return default             else:                 raise         return value      def get_raw_uri(self):         """         Return an absolute URI from variables available in this request. Skip         allowed hosts protection, so may return insecure URI.         """         return '{scheme}://{host}{path}'.format(             scheme=self.scheme,             host=self._get_raw_host(),             path=self.get_full_path(),         )      def build_absolute_uri(self, location=None):         """         Build an absolute URI from the location and the variables available in         this request. If no ``location`` is specified, bulid the absolute URI         using request.get_full_path(). If the location is absolute, convert it         to an RFC 3987 compliant URI and return it. If location is relative or         is scheme-relative (i.e., ``//example.com/``), urljoin() it to a base         URL constructed from the request variables.         """         if location is None:             # Make it an absolute url (but schemeless and domainless) for the             # edge case that the path starts with '//'.             location = '//%s' % self.get_full_path()         bits = urlsplit(location)         if not (bits.scheme and bits.netloc):             current_uri = '{scheme}://{host}{path}'.format(scheme=self.scheme,                                                            host=self.get_host(),                                                            path=self.path)             # Join the constructed URL with the provided location, which will             # allow the provided ``location`` to apply query strings to the             # base path as well as override the host, if it begins with //             location = urljoin(current_uri, location)         return iri_to_uri(location)      def _get_scheme(self):         """         Hook for subclasses like WSGIRequest to implement. Return 'http' by         default.         """         return 'http'      @property     def scheme(self):         if settings.SECURE_PROXY_SSL_HEADER:             try:                 header, value = settings.SECURE_PROXY_SSL_HEADER             except ValueError:                 raise ImproperlyConfigured(                     'The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.'                 )             if self.META.get(header) == value:                 return 'https'         return self._get_scheme()      def is_secure(self):         return self.scheme == 'https'      def is_ajax(self):         return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'      @property     def encoding(self):         return self._encoding      @encoding.setter     def encoding(self, val):         """         Set the encoding used for GET/POST accesses. If the GET or POST         dictionary has already been created, remove and recreate it on the         next access (so that it is decoded correctly).         """         self._encoding = val         if hasattr(self, 'GET'):             del self.GET         if hasattr(self, '_post'):             del self._post      def _initialize_handlers(self):         self._upload_handlers = [uploadhandler.load_handler(handler, self)                                  for handler in settings.FILE_UPLOAD_HANDLERS]      @property     def upload_handlers(self):         if not self._upload_handlers:             # If there are no upload handlers defined, initialize them from settings.             self._initialize_handlers()         return self._upload_handlers      @upload_handlers.setter     def upload_handlers(self, upload_handlers):         if hasattr(self, '_files'):             raise AttributeError("You cannot set the upload handlers after the upload has been processed.")         self._upload_handlers = upload_handlers      def parse_file_upload(self, META, post_data):         """Return a tuple of (POST QueryDict, FILES MultiValueDict)."""         self.upload_handlers = ImmutableList(             self.upload_handlers,             warning="You cannot alter upload handlers after the upload has been processed."         )         parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding)         return parser.parse()      @property     def body(self):         if not hasattr(self, '_body'):             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.             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:                 raise UnreadablePostError(*e.args) from e             self._stream = BytesIO(self._body)         return self._body      def _mark_post_parse_error(self):         self._post = QueryDict()         self._files = MultiValueDict()         self._post_parse_error = True      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:                 # An error occurred while parsing POST data. Since when                 # formatting the error the request handler might access                 # self.POST, set self._post and self._file to prevent                 # attempts to parse POST data again.                 # Mark that an error occurred. This allows self.__repr__ to                 # be explicit about it instead of simply representing an                 # empty POST                 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()      def close(self):         if hasattr(self, '_files'):             for f in chain.from_iterable(l[1] for l in self._files.lists()):                 f.close()      # File-like and iterator interface.     #     # Expects self._stream to be set to an appropriate source of bytes by     # a corresponding request subclass (e.g. WSGIRequest).     # Also when request data has already been read by request.POST or     # request.body, self._stream points to a BytesIO instance     # containing that data.      def read(self, *args, **kwargs):         self._read_started = True         try:             return self._stream.read(*args, **kwargs)         except IOError as e:             raise UnreadablePostError(*e.args) from e      def readline(self, *args, **kwargs):         self._read_started = True         try:             return self._stream.readline(*args, **kwargs)         except IOError as e:             raise UnreadablePostError(*e.args) from e      def __iter__(self):         while True:             buf = self.readline()             if not buf:                 break             yield buf      def xreadlines(self):         warnings.warn(             'HttpRequest.xreadlines() is deprecated in favor of iterating the '             'request.', RemovedInDjango30Warning, stacklevel=2,         )         yield from self      def readlines(self):         return list(self)
HttpRequest源码
from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt  # views.py @csrf_exempt def page(request):     # print(request.scheme)     # print(request.GET)     # print(request.POST)     # print(request.method)     # # print(request.encoding)     # print(request.environ)     # print(request.content_type)     # print(request.content_params)     # request.COOKIES["host"] = "whatever"     # print(request.COOKIES)     # print(request.FILES)     # print(request.FILES.get("dog"))     # print(request.META)     # print(request.resolver_match)     # request.session["username"] = "Jan"     # print(request.session["username"])     # print(request.get_signed_cookie("host"))     # print(request.is_secure())     # print(request.is_ajax())      # print(request.POST.get("age", None))     # print(request.get_port())     # print(request.get_full_path())     # print(request.path)     # print(request.path_info)     # print(request.get_host())     # print(request.GET.get("name", None))     return HttpResponse("OK!")

  使用httpie测试:

# GET请求带参数 http http://127.0.0.1:8000/page/ -- name=Jan # PSOT请求 http -f POST 127.0.0.1:8000/page/ age=20 # POST上传文件 http -f POST http://127.0.0.1:8000/page/  dog@Desktop/dog.png

  2、queryDict对象

  queryDict类是python字典的子类,具有字典的所有方法,它放在django.http.QueryDict中。它用“&”分割字符传,用“=”生成键值对,从而将一个类似GET请求参数的字符串解析成一个类似字典的对象。源码如下:

class QueryDict(MultiValueDict):     """     A specialized MultiValueDict which represents a query string.      A QueryDict can be used to represent GET or POST data. It subclasses     MultiValueDict since keys in such data can be repeated, for instance     in the data from a form with a <select multiple> field.      By default QueryDicts are immutable, though the copy() method     will always return a mutable copy.      Both keys and values set on this class are converted from the given encoding     (DEFAULT_CHARSET by default) to str.     """      # These are both reset in __init__, but is specified here at the class     # level so that unpickling will have valid values     _mutable = True     _encoding = None      def __init__(self, query_string=None, mutable=False, encoding=None):         super().__init__()         if not encoding:             encoding = settings.DEFAULT_CHARSET         self.encoding = encoding         query_string = query_string or ''         parse_qsl_kwargs = {             'keep_blank_values': True,             'fields_limit': settings.DATA_UPLOAD_MAX_NUMBER_FIELDS,             'encoding': encoding,         }         if isinstance(query_string, bytes):             # query_string normally contains URL-encoded data, a subset of ASCII.             try:                 query_string = query_string.decode(encoding)             except UnicodeDecodeError:                 # ... but some user agents are misbehaving :-(                 query_string = query_string.decode('iso-8859-1')         for key, value in limited_parse_qsl(query_string, **parse_qsl_kwargs):             self.appendlist(key, value)         self._mutable = mutable      @classmethod     def fromkeys(cls, iterable, value='', mutable=False, encoding=None):         """         Return a new QueryDict with keys (may be repeated) from an iterable and         values from value.         """         q = cls('', mutable=True, encoding=encoding)         for key in iterable:             q.appendlist(key, value)         if not mutable:             q._mutable = False         return q      @property     def encoding(self):         if self._encoding is None:             self._encoding = settings.DEFAULT_CHARSET         return self._encoding      @encoding.setter     def encoding(self, value):         self._encoding = value      def _assert_mutable(self):         if not self._mutable:             raise AttributeError("This QueryDict instance is immutable")      def __setitem__(self, key, value):         self._assert_mutable()         key = bytes_to_text(key, self.encoding)         value = bytes_to_text(value, self.encoding)         super().__setitem__(key, value)      def __delitem__(self, key):         self._assert_mutable()         super().__delitem__(key)      def __copy__(self):         result = self.__class__('', mutable=True, encoding=self.encoding)         for key, value in self.lists():             result.setlist(key, value)         return result      def __deepcopy__(self, memo):         result = self.__class__('', mutable=True, encoding=self.encoding)         memo[id(self)] = result         for key, value in self.lists():             result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))         return result      def setlist(self, key, list_):         self._assert_mutable()         key = bytes_to_text(key, self.encoding)         list_ = [bytes_to_text(elt, self.encoding) for elt in list_]         super().setlist(key, list_)      def setlistdefault(self, key, default_list=None):         self._assert_mutable()         return super().setlistdefault(key, default_list)      def appendlist(self, key, value):         self._assert_mutable()         key = bytes_to_text(key, self.encoding)         value = bytes_to_text(value, self.encoding)         super().appendlist(key, value)      def pop(self, key, *args):         self._assert_mutable()         return super().pop(key, *args)      def popitem(self):         self._assert_mutable()         return super().popitem()      def clear(self):         self._assert_mutable()         super().clear()      def setdefault(self, key, default=None):         self._assert_mutable()         key = bytes_to_text(key, self.encoding)         default = bytes_to_text(default, self.encoding)         return super().setdefault(key, default)      def copy(self):         """Return a mutable copy of this object."""         return self.__deepcopy__({})      def urlencode(self, safe=None):         """         Return an encoded string of all query string arguments.          `safe` specifies characters which don't require quoting, for example::              >>> q = QueryDict(mutable=True)             >>> q['next'] = '/a&b/'             >>> q.urlencode()             'next=%2Fa%26b%2F'             >>> q.urlencode(safe='/')             'next=/a%26b/'         """         output = []         if safe:             safe = force_bytes(safe, self.encoding)              def encode(k, v):                 return '%s=%s' % ((quote(k, safe), quote(v, safe)))         else:             def encode(k, v):                 return urlencode({k: v})         for k, list_ in self.lists():             k = force_bytes(k, self.encoding)             output.extend(encode(k, force_bytes(v, self.encoding))                           for v in list_)         return '&'.join(output)

  示例:

from django.http import QueryDict  # 添加django的环境配置 import os, django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "restful.settings") django.setup()  get_vactors = "a=1&a=2&a=3&b=Jan&b=Li&c=20" query = QueryDict(get_vactors, mutable=True) # print(query)  # 根据键生成新的QueryDict query_new = QueryDict.fromkeys(query, value="这个大傻子") # print(query_new)  # 获取键列表、值列表、键值对列表(值列表中的最后一个元素) print(list(query.items()))    # 生成器,获取包含键和最后一个值元素的元组的列表 print(list(query.keys()))     # dict_keys,获取键 print(list(query.values()))   # 生成器,获取每个键的最后一个元素值 # 键值对 print(list(query.lists()))    # 获取键键值对列表 print(dict(query))            # 转成字典,相当于query.dict()  # 获取单个元素及列表 alist = query.get("a") print(alist)  # 获取最后一个元素值 alist = query.getlist("a") print(alist)  # 获取键对应的value,返回元素列表  # 添加/修改键值对,必须将mutable设置为True query.setlist("a", [4, 5, 6]) query.setlistdefault("d", [4,5,6]) query.appendlist("e", ["this", "is", "append"]) print(query)  # 删除键值对 query.pop("e")   # 删除指定键值对 print(query) query.popitem()  # 删除最后一个键值对 print(query)

  3、HttpResponse

  HttpResponse类用于设置响应头和响应内容,同样封装在django.http模块中。它分为实例化对象、填充设置以及返回三个部分。它同时也是其它请求响应类的父类。

from django.http import HttpResponse  # 添加django的环境配置 import os, django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "restful.settings") django.setup()   # 实例化一: responseOne = HttpResponse("this is a http response") # 实例化二: responseTwo = HttpResponse() responseTwo.write("this is another http response") responseTwo.writelines(["this is second line", "this is third line"]) # 设置响应头 responseOne["Age"] = 20 responseOne["app"] = "sample" del responseOne["app"]  # 设置响应头 responseOne["content_type"] = 'application/vnd.ms-excel' responseOne['Content-Disposition'] = 'attachment; filename="foo.xls"'   responseOne.set_cookie("date", "2018-08-21", path="/page", )   # 设置cookie responseOne.delete_cookie("date")                              # 删除cookie # 有关对象 print(responseOne)                  # HttpResponse print(responseOne.items())          # dict_values print(responseOne.cookies)          # cookie print(responseOne.content)          # 内容(字节) print(responseOne.charset)          # 编码 print(responseOne.status_code)      # 状态码 print(responseOne.streaming)        # 是否为流 print(responseOne.closed)           # 是否已发送response print(responseOne.serialize())      # 序列化响应头和相应内容 print(responseOne.serialize_headers())  # 序列化响应头 print(responseOne.get("Age"))       # 获取响应头中的某个键值对 print(responseTwo.getvalue())       # 获取相应的内容  # 将response设置为流数据处理 responseTwo.readable() responseTwo.seekable() responseTwo.write("...")

  其它继承HttpResponse的子类包括:

HttpResponseRedirect                          # 重定向 HttpResponsePermanentRedirect                 # 永久重定向 HttpResponseNotModified                       # 304 HttpResponseBadRequest                        # 400 HttpResponseNotFound                          # 404 HttpResponseForbidden                         # 403 HttpResponseNotAllowed                        # 405 HttpResponseGone                              # 410 HttpResponseServerError                       # 500

  4、JsonResponse

  返回一个序列化的JSON对象。对于列表、字符串等,它会自动生成索引-元素JSON;对于字典,会直接生成相应的JSON。

class JsonResponse(HttpResponse):     """     An HTTP response class that consumes data to be serialized to JSON.      :param data: Data to be dumped into json. By default only ``dict`` objects       are allowed to be passed due to a security flaw before EcmaScript 5. See       the ``safe`` parameter for more information.     :param encoder: Should be a json encoder class. Defaults to       ``django.core.serializers.json.DjangoJSONEncoder``.     :param safe: Controls if only ``dict`` objects may be serialized. Defaults       to ``True``.     :param json_dumps_params: A dictionary of kwargs passed to json.dumps().     """      def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,                  json_dumps_params=None, **kwargs):         if safe and not isinstance(data, dict):             raise TypeError(                 'In order to allow non-dict objects to be serialized set the '                 'safe parameter to False.'             )         if json_dumps_params is None:             json_dumps_params = {}         kwargs.setdefault('content_type', 'application/json')         data = json.dumps(data, cls=encoder, **json_dumps_params)         super().__init__(content=data, **kwargs)

  DjangoJSONEncoder是内置的JSON编码方式,也支持传入自己写的编码方式对数据进行序列化。

# python manage.py shell from django.http import JsonResponse response = JsonResponse({'foo': 'bar'}) response.content
from django.http import  JsonResponse from django.views.decorators.csrf import csrf_exempt  @csrf_exempt def page(request):     # lis = list(range(10))     # return JsonResponse(lis, safe=False)     # dic = {"name": "Jan", "age": 20}     # return JsonResponse(dic)     string = "this is json response"     return JsonResponse(string, safe=False)

  5、FileResponse

from django.http import FileResponse response = FileResponse(open('myfile.png', 'rb'))

  

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!