python(十五):Django之HttpRequest
程序员文章站
2023-10-29 19:58:16
当一个请求连接进来时,django会创建一个HttpRequest对象来封装和保存所有请求相关的信息,并且会根据请求路由载入匹配的视图函数。每个请求的视图函数都会返回一个HttpResponse。 HttpRequest和HttpResponse可以从django.http中导入。 1、HttpRe ......
当一个请求连接进来时,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'))
上一篇: 夏季如何养胃 这些生活小秘诀呵护胃部健康
下一篇: 夏季养生各方面提醒
推荐阅读
-
python(十五):Django之HttpRequest
-
python(十四):Django之url和views
-
Python Django框架单元测试之文件上传测试示例
-
python 之 Django框架(路由系统、include、命名URL和URL反向解析、命名空间模式)
-
python3之Django表单(一)
-
Django基础之httprequest请求、响应、中间件
-
django 商城项目之购物车以及python中的一些redis命令
-
python之django母板页面的使用
-
荐 Python 框架 之 Django 的数据后台管理平台,简单的搭建、以及数据基本操作
-
python web开发之Django视图详解