当一个请求连接进来时,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 |
返回请求方式 |
|
返回一个字典,包含所有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)
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'))