Django(三):HttpRequest和HttpResponse

  當一個請求鏈接進來時,django會建立一個HttpRequest對象來封裝和保存全部請求相關的信息,而且會根據請求路由載入匹配的視圖函數。每一個請求的視圖函數都會返回一個HttpResponse。python

  HttpRequest和HttpResponse能夠從django.http中導入。ajax

  一、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請求

  示例:shell

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測試:django

# 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

  二、queryDict對象

  queryDict類是python字典的子類,具備字典的全部方法,它放在django.http.QueryDict中。它用「&」分割字符傳,用「=」生成鍵值對,從而將一個相似GET請求參數的字符串解析成一個相似字典的對象。源碼以下:json

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)

  三、HttpResponse

  HttpResponse類用於設置響應頭和響應內容,一樣封裝在django.http模塊中。它分爲實例化對象、填充設置以及返回三個部分。它同時也是其它請求響應類的父類。restful

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的子類包括:cookie

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

  四、JsonResponse

  返回一個序列化的JSON對象。對於列表、字符串等,它會自動生成索引-元素JSON;對於字典,會直接生成相應的JSON。session

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編碼方式,也支持傳入本身寫的編碼方式對數據進行序列化。app

# 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)

  五、FileResponse

from django.http import FileResponse response = FileResponse(open('myfile.png', 'rb'))
相關文章
相關標籤/搜索