一、HttpRequest对象
当请求一个页面时,Django创建一个HttpRequest对象。该对象包含request的元数据,然后Django调用相应的view函数(HttpRequest对象自动传递给该view函数<作为第一个参数>),每一个view负责返回一个HttpResponse对象。就像下面这个hello()函数:
1 2 3 4 |
from django.http import HttpResponse def hello(request): return HttpResponse("Hello world") |
下面解释HttpRequest和HttpResponse对象的API(属性),就是包含当前请求URL的一些信息。
1. HttpRequest属性
HttpRequest.scheme
一个字符串,表示请求的方案(通常是http或https)。
HttpRequest.path
请求页面的全路径,不包括域名。例如”/hello/world”。
HttpRequest.body
一个字符串,表示原始HTTP请求的正文。它对于处理非HTML形式的数据非常有用:二进制图像、XML等。 如果要处理常规的表单数据,应该使用HttpRequest.POST,因为Django已经将这些POST数据处理成QueryDict,而不是在body中的一节字符串。
你也可以使用”类文件“形式的接口从HttpRequest中读取数据。
HttpRequest.content_type
一个字符串,根据content_type参数获取MIME类型,比如浏览器访问是text/html,API访问时application/json。开发时可以根据这个来判断是什么设备访问,然后返回不同的数据。
HttpRequest.encoding
一个字符串,表示提交的数据的编码方式(如果为None则表示使用DEFAULT_CHARSET设置)。这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。接下来对属性的任何访问(例如从GET或POST中读取数据)将使用新的encoding值。如果你知道表单数据的编码不在DEFAULT_CHARSET中,则使用它。
HttpRequest.method
请求中使用的HTTP方法的字符串表示。全大写表示。例如:
1 2 3 4 |
if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else() |
HttpRequest.GET
包含所有HTTP GET参数的类字典对象,参见QueryDict文档。
HttpRequest.POST
包含所有HTTP POST参数的类字典对象。参见QueryDict文档。服务器收到空的POST请求的情况也是有可能发生的。也就是说,表单form通过HTTP POST方法提交请求,但是表单中可以没有数据。因此,不能使用语句if request.POST来判断是否使用HTTP POST方法;应该使用if request.method == “POST” (参见本表的method属性)。注意: POST不包括file-upload信息,参见FILES属性。
HttpRequest.COOKIES
包含所有cookies的标准Python字典对象,Keys和values都是字符串。
HttpRequest.session
一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django启用会话的支持时才可用。
HttpRequest.FILES
用来上传文件,包含所有上传文件的类字典对象,FILES中的每个Key都是<input type=”file” name=”” />标签中name属性的值。FILES中的每个value同时也是一个标准Python字典对象,包含下面三个Keys:
- filename:上传文件名,用Python字符串表示。
- content-type:上传文件的Content type。
- content:上传文件的原始内容。
注意:只有在请求方法是POST,并且请求页面中<form>有enctype=”multipart/form-data”属性时FILES才拥有数据。否则,FILES是一个空字典,上次不会成功。
HttpRequest.META
元数据信息,一个标准的Python字典,包含所有的HTTP头部。具体的头部信息取决于客户端和服务器,下面是一些示例:
- CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
- CONTENT_TYPE —— 请求的正文的MIME 类型。
- HTTP_ACCEPT —— 响应可接收的Content-Type。
- HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
- HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
- HTTP_HOST —— 客服端发送的HTTP Host头部。
- HTTP_REFERER —— Referring页面。
- HTTP_USER_AGENT —— 客户端的user-agent字符串。
- QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
- REMOTE_ADDR —— 客户端的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键。
HttpRequest.user
是一个django.contrib.auth.models.User对象,代表当前登录的用户。如果访问用户当前没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你可以通过user的is_authenticated()方法来辨别用户是否登录:
1 2 3 4 |
if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users. |
只有激活Django中的AuthenticationMiddleware时该属性才可用。
2. HttpRequest方法
HttpRequest.get_host()
根据从HTTP_X_FORWARDED_HOST(如果打开USE_X_FORWARDED_HOST)和HTTP_HOST 头部信息返回请求的原始主机。如果这两个头部没有提供相应的值,则使用SERVER_NAME和SERVER_PORT。
但当主机位于多个代理的后面,get_host()方法将会失败。有一个解决办法是使用中间件重写代理的头部。
HttpRequest.get_full_path()
返回path,如果可以将加上查询字符串。
例如:”/music/bands/the_beatles/?print=true”
HttpRequest.build_absolute_uri(location)
返回location的绝对URI。如果location没有提供,则设置为request.get_full_path()。
如果URI已经是一个绝对的URI,将不会修改。否则,使用请求中的服务器相关的变量构建绝对URI。例如:”http://example.com/music/bands/the_beatles/?print=true”。
HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt=”, max_age=None)
返回签名过的Cookie对应的值,如果签名不再合法则返回django.core.signing.BadSignature。如果提供default参数,将不会引发异常并返回default的值。
可选参数salt可以用来对安全密钥强力攻击提供额外的保护。max_age参数用于检查Cookie对应的时间戳以确保Cookie的时间不会超过max_age秒。
更多信息参见密钥签名。
HttpRequest.is_secure()
如果请求时是安全的,则返回True;即请求是通过HTTPS发起的。
HttpRequest.is_ajax()
如果请求是通过XMLHttpRequest发起的,则返回True,方法是检查HTTP_X_REQUESTED_WITH头部是否是字符串’XMLHttpRequest’。大部分现代的JavaScript库都会发送这个头部。如果你编写自己的XMLHttpRequest调用(在浏览器端),你必须手工设置这个值来让is_ajax() 可以工作。
如果一个响应需要根据请求是否是通过AJAX发起的,并且你正在使用某种形式的缓存例如Django的cache middleware, 你应该使用vary_on_headers(‘HTTP_X_REQUESTED_WITH’) 装饰你的视图以让响应能够正确地缓存。
二、QueryDict对象
在HttpRequest对象中,属性GET和POST得到的都是django.http.QueryDict所创建的实例。这是一个django自定义的类似字典的类,用来处理同一个键带多个值的情况。
在Python原始的字典中,当一个键出现多个值的时候会发生冲突,只保留最后一个值。而在HTML表单中,通常会发生一个键有多个值的情况,例如<select multiple>(多选框)就是一个很常见情况。
Django提供了QueryDict API,可以用来测试这些方法,进入django shell环境即可:
1 2 |
$ python manage.py shell >>> from django.http import QueryDict |
下面我们来看这个类中有什么方法:
1 |
QueryDict.__init__(query_string=None, mutable=False, encoding=None) |
这是一个构造函数,其中query_string需要一个字符串,例如:
1 2 |
>>> QueryDict('a=1&a=2&c=3') <QueryDict: {'a': ['1', '2'], 'c': ['3']}> |
如果query_string没有传入,则获得一个空的对象。
你所遇到的QueryDict对象,特别是request.POST和request.GET得到的。如果你想自己实例化一个对象,可以传递mutable=True使你所实例化的对象可变。当然request.POST和request.GET是django创建的,也就是说除非改django源码,否则它们是不可变的。
对于设置的键和值,会从encoding转码成Unicode。也就是说,如果传入的字符串query_string是GBK或者是utf-8的编码,将会自动转码成Unicode,然后用做字典的键和值。如果encoding = None,也就是没有设定的话,将使用DEFAULT_CHARSET的值,默认为:’utf-8’。
QueryDict实现所有标准的字典方法,还包括一些特有的方法:
QueryDict.__getitem__(key)
返回给出的key的值。如果key具有多个值,__getitem__()返回最后(最新)的值。如果key不存在,则引发django.utils.datastructures.MultiValueDictKeyError。(它是Python标准KeyError的一个子类,所以你仍然可以坚持捕获KeyError。)
1 2 3 |
>>> q = QueryDict('a=1&a=2&c=3') >>> q.__getitem__('a') '2' |
QueryDict.__setitem__(key, value)
设置给出的key的值为[value](一个Python列表,只有一个元素value)。注意:只有对象是可以改变的时候才能使用,例如通过.copy()方法创建的对象,或者设置为可变类型。
1 2 3 4 5 6 7 8 9 10 |
>>> q = QueryDict('a=1') >>> d = q.copy() >>> d.__setitem__('a',2) >>> d.get('a') 2 >>> q = QueryDict('a=1', mutable=True) >>> q.__setitem__('a',2) >>> q <QueryDict: {'a': [2]}> |
QueryDict.__contains__(key)
如果给出的key已经设置,则返回True。它让你可以做if “foo” in request.GET这样的操作。
1 2 3 4 |
>>> q.__contains__('a') True >>> q.__contains__('b') False |
QueryDict.get(key, default)
使用与上面__getitem__()相同的逻辑,但是当key不存在时返回一个默认值。
1 2 |
>>> q.get('key', 0) 0 |
QueryDict.setdefault(key, default)
类似标准字典的setdefault()方法,只是它在内部使用的是__setitem__()。也就是说,当key已经存在时,返回其值,key不存在时,返回default,同时添加key和default到对象中。
QueryDict.update(other_dict)
接收一个QueryDict或标准字典。类似标准字典的update()方法,但是它附加到当前字典项的后面,而不是替换掉它们。
1 2 3 4 5 6 |
>>> q = QueryDict('a=1', mutable=True) #当然要可变的才能使用; >>> q.update({'a': '2'}) >>> q.getlist('a') ['1', '2'] >>> q['a'] # returns the last ['2'] |
QueryDict.items()
类似标准字典的items()方法,返回一个迭代对象。但是它使用的是和__getitem__一样返回最新的值的逻辑。
1 2 3 4 5 6 7 8 9 |
>>> q = QueryDict('a=1&a=2&b=3') >>> m = q.items() >>> m.__name__ '_iteritems' >>> m.__next__() ('b', '3') >>> m.__next__() ('a', '2') >>> m.__next__() #会抛异常; |
QueryDict.lists()
类似QueryDict.iteritems(),返回一个包含键值对的元祖(key, value)迭代对象 ,value是一个包括所有key的值的列表。
1 2 3 4 5 6 |
>>> q = QueryDict('a=1&a=2&b=3') >>> m = q.lists() >>> m.__next__() ('a', ['1', '2']) >>> m.__next__() ('b', ['3']) |
QueryDict.values()
类似标准字典的values()方法,但是它使用的是和__getitem__一样返回最新的值的逻辑。也就是返回一个所有键对应的最新值的列表。
1 2 3 4 5 6 |
>>> q = QueryDict('a=1&a=2&b=3') >>> m = q.values() >>> m.__next__() '3' >>> m.__next__() '2' |
QueryDict.copy()
返回对象的副本,使用Python标准库中的copy.deepcopy()。此副本是可变的,即使原始对象是不可变的。
1 2 3 4 |
>>> q = QueryDict('a=1&a=2&c=3') >>> m = q.copy() >>> m <QueryDict: {'a': ['1', '2'], 'c': ['3']}> |
QueryDict.getlist(key, default)
以Python列表形式返回所请求的键的数据。如果键不存在并且没有提供默认值,则返回空列表。它保证返回的是某种类型的列表,除非默认值不是列表。
1 2 3 4 5 |
>>> q = QueryDict('a=1&a=2&b=3') >>> q.getlist('a') ['1', '2'] >>> q.getlist('c', 0) 0 |
QueryDict.setlist(key, list_)
为给定的键设置list_(与__setitem__()不同),可以设置一个多元素的列表。
QueryDict.appendlist(key, item)
将项追加到内部与键相关联的列表中。
QueryDict.setlistdefault(key, default_list)
类似setdefault,除了它接受一个列表而不是单个值。
QueryDict.pop(key)
返回给定键的值的列表,并从字典中移除它们。如果键不存在,将引发KeyError。
1 2 3 |
>>> q = QueryDict('a=1&a=2&a=3', mutable=True) >>> q.pop('a') ['1', '2', '3'] |
QueryDict.popitem()
删除字典任意一个成员(因为没有顺序的概念),并返回二值元组,包含键和键的所有值的列表。在一个空的字典上调用时将引发KeyError。
1 2 3 4 5 |
>>> q = QueryDict('a=1&a=2&b=3', mutable=True) >>> q.popitem() ('a', ['1', '2']) >>> q <QueryDict: {'b': ['3']}> |
QueryDict.dict()
返回QueryDict的dict表示形式。对于QueryDict中的每个(key, list)对 ,dict将有(key, item) 对,其中item是列表中的一个元素,使用与QueryDict.__getitem__()相同的逻辑,也就是最新的:
1 2 3 |
>>> q = QueryDict('a=1&a=3&a=5') >>> q.dict() {'a': '5'} |
QueryDict.urlencode([safe])
从数据中返回查询字符串格式。
1 2 3 |
>>> q = QueryDict('a=2&b=3&b=5') >>> q.urlencode() 'a=2&b=3&b=5' |
可选地,urlencode可以传递不需要编码的字符。(这意味着要进行url编码)
1 2 3 4 |
>>> q = QueryDict(mutable=True) >>> q['next'] = '/a&b/' >>> q.urlencode(safe='/') 'next=/a%26b/' |
三、HttpResponse对象
对于HttpRequest对象来说,是由Django自动创建。但是,HttpResponse对象就必须我们自己创建。每个View方法必须返回一个HttpResponse对象。
HttpResponse类在django.http.HttpResponse。
1. HttpResponse构造
一般地, 你可以通过给HttpResponse的构造函数传递字符串表示的页面内容来构造HttpResponse对象:
1 2 3 4 5 6 |
$ python manage.py shell >>> from django.http import HttpResponse >>> response = HttpResponse("Here's the text of the Web page.") >>> response response <HttpResponse status_code=200, "text/html; charset=utf-8"> >>> response = HttpResponse("Text only, please.", content_type="text/plain") |
但是如果想要增量添加内容, 你可以把response当作filelike对象使用:
1 2 3 |
>>> response = HttpResponse() >>> response.write("<p>Here's the text of the Web page.</p>") >>> response.write("<p>Here's another paragraph.</p>") |
也可以给HttpResponse传递一个iterator作为参数,而不用传递硬编码字符串。 如果你使用这种技术, 下面是需要注意的一些事项:
- iterator应该返回字符串。
- 如果HttpResponse使用iterator进行初始化,就不能把HttpResponse实例作为filelike 对象使用。这样做将会抛出异常。
最后,再说明一下,HttpResponse实现了write()方法, 可以在任何需要filelike对象的地方使用HttpResponse对象。
你可以使用字典语法添加,删除headers:
1 2 3 |
<span class="gp">>>> </span><span class="n">response</span> <span class="o">=</span> <span class="n">HttpResponse</span><span class="p">()</span> <span class="gp">>>> </span><span class="n">response</span><span class="p">[</span><span class="s1">'Age'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">120</span> <span class="gp">>>> </span><span class="k">del</span> <span class="n">response</span><span class="p">[</span><span class="s1">'Age'</span><span class="p">]</span> |
2. HttpResponse属性
- HttpResponse.charset
表示将在其中对响应进行编码的字符集。如果在http实例化时没有给出,它将从content_type中提取,如果不成功,将使用DEFAULT_CHARSET设置。
- HttpResponse.status_code
响应HTTP的状态代码。除非显式设置了reason_phrase,否则修改构造函数外部的status_code值也将修改reason_phrase的值。
- HttpResponse.reason_phrase
响应HTTP的reason_phrase。它使用HTTP标准的默认原因短语。除非显式设置,否则reason_phrase是由status_code的值决定的。
- HttpResponse.streaming
默认为False。因此属性存在,因此中间件可以以不同于常规响应的方式处理流响应。
- HttpResponse.closed
如果响应已关闭,则为True。
3. HttpResponse方法
- HttpResponse.__init__(content=”, content_type=None, status=200, reason=None, charset=None)
HttpResponse使用给定的页面内容和内容类型实例化一个对象。
content:应该是一个迭代器或一个字符串。如果它是一个迭代器,它应该返回字符串,并且这些字符串将被连接在一起以形成响应的内容。如果它不是迭代器或字符串,它将在访问时转换为字符串。
content_type:是可选地由字符集编码完成的MIME类型,并用于填充HTTP Content-Type标头。如果未指定,则由默认设置DEFAULT_CONTENT_TYPE和DEFAULT_CHARSET设置组成:“text / html; charset = utf-8“。
status:是个响应的HTTP状态码。
reason:是HTTP响应短语,如200状态码的短语是Ok,如果未提供,将使用默认短语。
charset:是响应将被编码的字符集。如果没有给出,它将被提取content_type,如果不成功,DEFAULT_CHARSET将使用该设置。
- HttpResponse.set_cookie(key, value=”, max_age=None, expires=None, path=’/’, domain=None, secure=None, httponly=False)
设置cookie。
- HttpResponse.set_signed_cookie(key, value, salt=”, max_age=None, expires=None, path=’/’, domain=None, secure=None, httponly=True)
设置带签名的cookie。
- HttpResponse.delete_cookie(key, path=’/’, domain=None)
删除cookie。
- HttpResponse.getvalue()
返回HttpResponse.content。
- HttpResponse.__setitem__(header, value)
设置响应头属性。
- HttpResponse.__delitem__(header)
删除响应头属性。
- HttpResponse.__getitem__(header)
访问响应头属性。
- HttpResponse.has_header(header)
检查是否存在响应头属性。
4. HttpResponse子类
Django包含很多HttpResponse子类,用来处理不同的HTTP响应类型,和HttpResponse一样,这些子类在django.http中。
- HttpResponseRedirect
构造函数接受单个参数,重定向到的URL。可以是全URL (e.g., ‘http://search.yahoo.com/’)或者相对URL(e.g., ‘/search/’),注意:这将返回HTTP状态码302。
- HttpResponsePermanentRedirect
同HttpResponseRedirect一样,但是返回永久重定向(HTTP状态码301)。
- HttpResponseNotModified
构造函数不需要参数。使用此项可指定自用户上次请求以来页面未被修改。
- HttpResponseBadRequest
返回400 status code。
- HttpResponseNotFound
返回404 status code。
- HttpResponseForbidden
返回403 status code。
- HttpResponseNotAllowed
返回405 status code,它需要一个必须的参数:一个允许的方法的list (e.g., [‘GET’,’POST’])。
- HttpResponseGone
返回410 status code。
- HttpResponseServerError
返回500 status code。
当然,你也可以自己定义不包含在上表中的HttpResponse子类。
在Django中返回HTTP错误码是很容易的。上面介绍了HttpResponseNotFound,HttpResponseForbidden,HttpResponseServerError等一些子类。View方法中返回这些子类的实例就OK了,例如:
1 2 3 4 5 6 |
def my_view(request): # ... if foo: return HttpResponseNotFound('<h1>Page not found</h1>') else: return HttpResponse('<h1>Page was found</h1>') |
另外在Django中,默认提供了常见的错误处理方式,结合这些子类可以返回一些默认错误页面。 详见:Django自定义错误视图
四、JsonResponse对象
JsonResponse也是HttpResponse的子类,用于创建JSON编码响应的HttpResponse子类,用于生成一个Json格式而非html/xml的响应格式。
class JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs)
第一个参数,即数据,应该是一个字典实例。如果safe参数设置为False (请参见下文),它可以是任何JSON可序列化的对象。
其默认Content-Type headers设置为 “application/json”。
encoder参数默认为django.core.serializers.json.DjangoJSONEncoder编码器,将用于序列化数据。有关此序列化程序的更多详细信息, 请参见Django系列化。
safe的参数默认为True,如果设置为False,则可以为序列化传递任何对象 (否则只允许字典实例)。如果safe为true,并且将non-dict对象作为第一个参数传递,则将引发TypeError异常。
json_dumps_params参数的字典或关键字参数是要传递给json.dumps()调用,用于生成响应。
1 2 3 4 |
>>> from django.http import JsonResponse >>> response = JsonResponse({'foo': 'bar'}) >>> response.content b'{"foo": "bar"}' |
Serializing non-dictionary objects
1 |
>>> response = JsonResponse([1, 2, 3], safe=False) |
Without passing safe=False, a TypeError will be raised。
官网文档:https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpRequest