【图书介绍】《Django 5企业级Web应用开发实战(视频教学版)》_django 5企业级web应用开发实战(视频教学版)-CSDN博客
《Django 5企业级Web应用开发实战(视频教学版)》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com)
在Django框架中,HttpRequest对象是由HttpRequest类来定义的。HttpRequest对象是组成HTTP数据包的核心部件之一,其中包含了非常多的、十分重要的信息和数据。
每当一个客户端请求发送过来时,Django框架负责将HTTP数据包中的相关内容打包成为一个HttpRequest对象,并传递给相关视图函数作为第一位置参数(request)来调用。
HttpRequest类的属性有3类:由Django框架自身设置的属性、由应用代码设置的属性和由中间件设置的属性。
1. 由Django框架自身设置的属性
HttpRequest类的大部分属性均是只读(readonly)的,除非特别注明的属性。该类属性的详细内容介绍如下:
(1)HttpRequest.scheme:字符串类型,表示请求的协议种类,通常为“http”或“https”。
(2)HttpRequest.body:Bytes类型,表示原始HTTP请求的正文。该属性对于处理非HTML形式的数据(例如二进制图像、XML等)非常有用。注意,如果要处理常规的表单数据,应该使用下面将要介绍的HttpRequest.POST。
另外,还可以使用类似读写文件的方式从HttpRequest中读取数据,参见下面将要介绍的HttpRequest.read()。
(3)HttpRequest.path:字符串类型,表示当前请求页面的完整路径,但是不包括协议名和域名(例如“/article/authors/python/”)。
该属性非常有用,常用于在进行某项操作时,如果执行不通过就返回用户先前浏览的页面。
(4)HttpRequest.path_info:在某些Web服务器的配置中,主机名后的URL部分会被分成脚本前缀和路径信息这两个部分。path_info属性的作用是,不论使用的Web服务器是什么,该属性将始终包含路径信息部分。因此,使用该属性代替path可以保证代码在测试和开发环境中更容易地进行切换。
举例来讲,如果Web服务器配置中的WSGIScriptAlias参数设置为“/mydb”,那么当HttpRequest.path为“/article/authors/python/”时,HttpRequest.path_info则为“/mydb/article/authors/python/”。
(5)HttpRequest.method:字符串类型,表示请求使用的HTTP方法,默认为大写。具体使用方法如【代码4-55】所示。
【代码4-55】
01 if request.method == 'GET':
02 do_something()
03 elif request.method == 'POST':
04 do_something_else()
【代码分析】
通过HttpRequest对象的method属性判断请求的方法,然后根据请求方法的不同,在视图中执行不同的代码。
(6)HttpRequest.encoding:字符串类型,表示提交数据的编码方式(如果属性值为None,则表示使用DEFAULT_CHARSET设置)。
该属性是可写(编辑)的,可以通过修改该属性来改变表单数据的编码。另外,对于任何随后的属性访问(例如GET或POST),将会使用新的编码方式。
(7)HttpRequest.content_type:表示从CONTENT_TYPE头解析的请求的MIME类型。
(8)HttpRequest.content_params:包含在CONTENT_TYPE头标题中的键-值对参数字典。
(9)HttpRequest.GET:一个类似于字典的对象,包含GET请求中的所有参数。
举例来讲,在链接地址“http://example.com/?name=jack&age=18”中,“name=jack”和“age=18”就是字典对象类型的键-值对参数。
(10)HttpRequest.POST:包含所有POST表单数据的字典对象类型的键-值对参数。如果需要访问请求中的原始或非表单数据,可以使用HttpRequest.body属性。
注意,使用if条件语句通过判断“request.method=="POST"”来甄别一个请求是否为POST类型,而不要直接使用request.POST进行判断。
此外,POST中不包含上传的文件数据。
(11)HttpRequest.COOKIES:该属性包含所有Cookie信息的字典,键-值对均为字符串。可以使用类似字典类型的方式在Cookie中读写数据。注意,Cookie是不安全的,因此不要写敏感重要的信息。
(12)HttpRequest.FILES:该属性为一个类似于字典的对象,包含所有上传的文件数据。HttpRequest.FILES中的每个键为“<input type="file" name="" />”中的name属性值,对应的每个值为一个UploadedFile对象。
在Django框架中,实现文件上传功能主要依靠该属性。如果请求方式是POST且请求的<form>中带有“enctype="multipart/form-data"”属性,则HttpRequest.FILES属性将包含上传的文件数据;否则,HttpRequest.FILES将为一个空的类似于字典的对象,属于被忽略、无用的情形。
(13)HttpRequest.META:该属性为一个包含所有HTTP头部信息的字典。可用的头部信息取决于客户端和服务器,具体示例如下:
- CONTENT_LENGTH:请求正文的长度(以字符串计)。
- CONTENT_TYPE:请求正文的MIME类型。
- HTTP_ACCEPT:可接收的响应Content-Type。
- HTTP_ACCEPT_ENCODING:可接收的响应编码类型。
- HTTP_ACCEPT_LANGUAGE:可接收的响应语言种类。
- HTTP_HOST:客服端发送的HOST头部。
- HTTP_REFERER:Referring页面。
- HTTP_USER_AGENT:客户端的“user-agent”字符串。
- QUERY_STRING:查询字符串。
- REMOTE_ADDR:客户端的IP地址,可以获取客户端的IP信息。
- REMOTE_HOST:客户端的主机名。
- REMOTE_USER:服务器认证后的用户,前提是用户可用。
- REQUEST_METHOD:表示请求方式的字符串,例如“GET”或“POST”。
- SERVER_NAME:服务器的主机名。
- SERVER_PORT:服务器的端口(字符串)。
以上介绍的是比较重要和比较常用的头部信息,完整详细的信息请参考官方文档。
根据上述内容可知,除CONTENT_LENGTH和CONTENT_TYPE之外,请求中的任何HTTP头部键在转换为META键时,都会将所有字母转为大写并将连接符替换为下画线最后加上“HTTP_”前缀。因此,一个叫作X-Bender的头部将转换成META中的HTTP_X_BENDER键。
(14)HttpRequest.headers:该属性是一个不区分大小写的、类似dict的对象,包含请求中HTTP头部的所有信息。
(15)HttpRequest.resolver_match:该属性用于对请求中的URL地址进行解析,获取一些相关的信息(例如namespace等)。
在视图中通过request.resolver_match.namespace的方式来访问。
1)由应用代码设置的属性
这类属性不由Django框架自身进行设置,而使用应用代码进行设置。该类属性的详细内容介绍如下:
(1)HttpRequest.current_app:该属性表示当前应用(app)的名字。在url模板标签中,将使用该属性值作为reverse()方法的current_app参数。
(2)HttpRequest.urlconf:该属性设置当前请求的根URLconf模块,用于指定不同的URL路由入口,这将覆盖settings配置中ROOT_URLCONF参数的设置。将该属性值修改为None,将可恢复对ROOT_URLCONF参数的设置。
(3)HttpRequest.exception_reporter_filter:该属性用于替代当前请求中的DEFAULT_EXCEPTION_ REPORTER_FILTER。
(4)HttpRequest.exception_reporter_class:该属性用于替代当前请求中的DEFAULT_EXCEPTION_ REPORTER。
2)由中间件设置的属性
这类属性是在Django框架中由contrib应用中包含的一些中间件在请求上设置的。如果在请求中没有看到这些属性,那么需确认正确的中间件类名是否在MIDDLEWARE列表中。该类属性的详细内容介绍如下:
(1)HttpRequest.session:该属性来自SessionMiddleware中间件,是一个可读写的、类似字典的对象,表示当前会话。如果要保存用户状态、回话过程等,需要的就是这个中间件和这个属性。
(2)HttpRequest.site:该属性来自CurrentSiteMiddleware中间件,是get_current_site()方法返回的Site对象或RequestSite对象的实例,代表当前具体站点。
另外,Django框架是支持多站点的。如果同时上线了几个站点,就需要为每个站点设置一个站点id。
(3)HttpRequest.user:该属性来自AuthenticationMiddleware中间件,表示当前登录的用户的AUTH_USER_MODEL实例。该模型是Django框架内置的Auth模块下的User模型。如果用户当前未登录,则user将被设置为AnonymousUser对象的实例。
在实际应用中,可以通过is_authenticated方法判断当前用户是否为合法用户,示例代码如下:
【代码4-56】
01 if request.user.is_authenticated:
02 ... # 为已登录用户执行某些操作
03 else:
04 ... # 为匿名用户执行某些操作
【代码分析】
通过HttpRequest对象中user属性的is_authenticated方法判断当前用户是否为合法用户,然后根据登录用户或匿名用户在视图中执行不同的代码。
2. 方法
(1)HttpRequest.get_host()方法:该方法返回根据HTTP_X_FORWARDED_HOST(前提是被允许)和HTTP_HOST头部信息获取的请求的原始主机。如果这两个头部信息没有提供相应的值,则使用SERVER_NAME和SERVER_PORT头部信息。
举例来讲,当主机位于多个代理的后面时,get_host()方法将会失败。解决办法之一就是使用中间件重写代理的头部,示例代码如下:
【代码4-57】
01 class MultipleProxyMiddleware:
02 FORWARDED_FOR_FIELDS = [
03 'HTTP_X_FORWARDED_FOR',
04 'HTTP_X_FORWARDED_HOST',
05 'HTTP_X_FORWARDED_SERVER',
06 ]
07
08 def __init__(self, get_response):
09 self.get_response = get_response
10
11 def __call__(self, request):
12 """
13 Rewrites the proxy headers so that only the most
14 recent proxy is used.
15 """
16 for field in self.FORWARDED_FOR_FIELDS:
17 if field in request.META:
18 if ',' in request.META[field]:
19 parts = request.META[field].split(',')
20 request.META[field] = parts[-1].strip()
21 return self.get_response(request)
【代码分析】
这段代码中定义的中间件MultipleProxyMiddleware,将在其他中间件(从get_host()方法的返回值中获取的,例如CommonMiddleware中间件或CsrfViewMiddleware中间件)之前被定位。
(2)HttpRequest.get_port()方法:该方法使用META中的HTTP_X_FORWARDED_PORT(前提是被允许)和SERVER_PORT的信息返回请求的始发端口。
(3)HttpRequest.get_full_path()方法:该方法返回包含完整参数列表的路径path,例如“/article/authors/python/?print=true”。
(4)HttpRequest.build_absolute_uri(location)方法:该方法返回location的绝对URI形式。如果location没有提供,则使用request.get_full_path()方法获取的值。例如“https://example.com/article/ authors/python/?print=true”。
一般不建议在同一站点上混合部署HTTP和HTTPS,如果需要将用户重定向到HTTPS,最好使用Web服务器将所有HTTP流量重定向到HTTPS。
(5)HttpRequest.get_signed_cookie(key,default=RAISE_ERROR,salt='',max_age=None)方法:该方法从已签名的Cookie中获取值,如果签名不合法则返回django.core.signing.BadSignature对象。其中,可选参数salt用来为密码提供额外保护,从而提高安全系数;max_age参数用于检查Cookie对应的时间戳是否超时。官方文档给出的应用实例如下:
>>> request.get_signed_cookie('name')
'Tony'
>>> request.get_signed_cookie('name', salt='name-salt')
'Tony' # 假设使用相同的salt设置了Cookie
>>> request.get_signed_cookie('non-existing-cookie')
...
KeyError: 'non-existing-cookie'
>>> request.get_signed_cookie('non-existing-cookie', False)
False
>>> request.get_signed_cookie('cookie-that-was-tampered-with')
...
BadSignature: ...
>>> request.get_signed_cookie('name', max_age=60)
...
SignatureExpired: Signature age 1677.3839159 > 60 seconds
>>> request.get_signed_cookie('name', False, max_age=60)
False
(6)HttpRequest.is_secure()方法:如果使用的是HTTPS,则该方法返回True,表示链接是安全的。
(7)HttpRequest.accepts(mime_type)方法:如果请求头部接收的类型匹配mime_type参数,则该方法返回True,否则返回False。该方法是Django v3.1版本的新增功能。官方文档给出的应用实例如下:
>>> request.accepts('text/html')
True
(8)HttpRequest.read(size=None)方法、HttpRequest.readline()方法、HttpRequest.readlines()方法和HttpRequest.__iter__()方法:这4个方法都是从HttpRequest实例读取文件数据的方法,可以将HttpRequest实例直接传递到XML解析器。请看下面关于解析ElementTree(元素树)的代码实例。
【代码4-58】
01 import xml.etree.ElementTree as ET
02 for element in ET.iterparse(request):
03 process(element)