HttpRequestã使ãããªã
ãHttpRequestã®è¸ã¿è¾¼ãã 解説ã«å ¥ãåã«ãç°¡åã«HTTPã®åºæ¬ããããããã¾ããcurlãç¨ãã¦ãDjangoã®ã¹ã¿ã¼ããã¼ã¸ã«ãªã¯ã¨ã¹ããé£ã°ãã¦ã¿ã¾ãã
$ curl -v http://127.0.0.1:8000/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0) > GET / HTTP/1.1 > Host: 127.0.0.1:8000 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 16 Mar 2021 19:38:40 GMT < Server: WSGIServer/0.2 CPython/3.9.0 < Content-Type: text/html < X-Frame-Options: DENY < Content-Length: 10697 < X-Content-Type-Options: nosniff < Referrer-Policy: same-origin < <!doctype html> <html lang="en-us" dir="ltr"> (ä¸ç¥) </html> * Connection #0 to host 127.0.0.1 left intact * Closing connection 0
ããã®ããã«-vãªãã·ã§ã³ãã¤ãããã¨ã§ããªã¯ã¨ã¹ããã¬ã¹ãã³ã¹ã®è©³ç´°ã確èªã§ãã¾ããcurlãéä¿¡ããHTTPã®ãªã¯ã¨ã¹ãã¯ã次ã®ããã«ãªã£ã¦ãã¾ãã
GET / HTTP/1.1 Host: 127.0.0.1:8000 User-Agent: curl/7.64.1 Accept: */* <EOF>
ãHTTPãªã¯ã¨ã¹ãã®1è¡ç®ã¯ããªã¯ã¨ã¹ãã©ã¤ã³ï¼Request-Lineï¼ã¨å¼ã°ãã¾ãããªã¯ã¨ã¹ãã©ã¤ã³ã¯ãã¡ã½ãããããªã¯ã¨ã¹ãURIããHTTPãã¼ã¸ã§ã³ããããªãã¾ããä»åã®å ´åãã¡ã½ããã¯ãGETãããªã¯ã¨ã¹ãURIã¯ã/ãããããã³ã«ãã¼ã¸ã§ã³ã¯HTTPã®1.1ã§ãããªã¯ã¨ã¹ãã©ã¤ã³ã®æ¬¡ã«HTTPãããã¼ã®å¤ãç¶ãã空è¡ã1è¡ããã¦ã¡ãã»ã¼ã¸ããã£ã¼ï¼Message bodyï¼ãããã¾ããä»åã¯ã¡ãã»ã¼ã¸ããã£ã¼ã¯ç©ºã§ãã次ã¯ãDjangoã®ã¢ããªã±ã¼ã·ã§ã³ãè¿ããHTTPã¬ã¹ãã³ã¹ã確èªãã¾ãããã
HTTP/1.1 200 OK Date: Tue, 16 Mar 2021 19:39:14 GMT Server: WSGIServer/0.2 CPython/3.9.0 Content-Type: text/html X-Frame-Options: DENY Content-Length: 10697 X-Content-Type-Options: nosniff Referrer-Policy: same-origin <!doctype html> <html> ... </html>
ãHTTPã¬ã¹ãã³ã¹ã®1è¡ç®ã¯ãã¹ãã¼ã¿ã¹ã©ã¤ã³ï¼Status-Lineï¼ã¨å¼ã°ãã¾ããã¹ãã¼ã¿ã¹ã©ã¤ã³ã¯ãHTTPãã¼ã¸ã§ã³ããã¹ãã¼ã¿ã¹ã³ã¼ããããªã¼ãºã³ãã¬ã¼ãºï¼Reason-Phraseï¼ããããªãã¾ããã¹ãã¼ã¿ã¹ã³ã¼ãã200çªå°ã§ãããã¨ãããæ£å¸¸ã«ã¬ã¹ãã³ã¹ãè¿ã£ã¦ãã¦ãããã¨ã確èªã§ãã¾ããç¶ãã¦HTTPãããã¼ã®å¤ãç¶ãã空è¡ã1è¡ããã¦ã¡ãã»ã¼ã¸ããã£ã¼ãããã¾ããã¡ãã»ã¼ã¸ããã£ã¼ã«ã¯HTMLãè¨è¿°ããã¦ãã¾ããä»åã¯curlã§HTTPãªã¯ã¨ã¹ããéä¿¡ãã¾ããããWebãã©ã¦ã¶ã§ã¢ã¯ã»ã¹ããéãåºæ¬çã«ã¯å¤ããã¾ãããæµããå³ã«ããã¨å³1ã®ããã«ãªãã¾ãã

ãããã¾ã§ã®æµããé ã«å ¥ãã¦ããã°ãHttpRequestã®ç解ã¯ããã»ã©é£ããããã¾ãããWSGIãµã¼ãã¼ãDjangoã¢ããªã±ã¼ã·ã§ã³ã«ãã£ã¦ãHTTPã®ãªã¯ã¨ã¹ãã¯å³2ã«ç¤ºãããã«HttpRequestãªãã¸ã§ã¯ãã«å¤æãããHttpResponseãªãã¸ã§ã¯ãã«æ¸ãè¾¼ãã å¤ã¯å³3ã«ç¤ºãããã«HTTPã®ã¬ã¹ãã³ã¹ã¨ãã¦ã¯ã©ã¤ã¢ã³ãã«è¿ããã¾ãã


x-www-form-urlencodedå½¢å¼ã®ãã¼ã¿ã®èªã¿è¾¼ã¿
ãHTTPãªã¯ã¨ã¹ãã®ã¡ãã»ã¼ã¸ããã£ã¼ãåå¾ããæ¹æ³ã¯è¤æ°ç¨æããã¦ãã¾ãã
- request.bodyï¼ãªã¯ã¨ã¹ãããã£ã¼ï¼ä¾ï¼b'{"message": "Hello World"'ï¼
- request.readã¡ã½ããï¼file-likeãªãã¸ã§ã¯ã
- request.POSTï¼x-www-form-urlencodedå½¢å¼ã®ãªã¯ã¨ã¹ãããã£ã¼ï¼ä¾ï¼QueryDict({'email': ['[email protected]']})ï¼
- request.FILESï¼multipart/form-dataå½¢å¼ã®ãªã¯ã¨ã¹ãããã£ã¼ï¼ä¾ï¼MultiValueDict({'user': ['shibata']})ï¼
ãã¡ãã»ã¼ã¸ããã£ã¼ã¯ãrequest.bodyããã®ãã¤ãæååã§åå¾ã§ãã¾ãããããã¡ãã»ã¼ã¸ããã£ã¼ã®ãµã¤ãºã大ããä¸åº¦ã«ã¡ã¢ãªã«è¼ããããªãã±ã¼ã¹ã§ã¯ãrequest.readã¡ã½ããã«ããfile-likeãªãã¸ã§ã¯ããåå¾ãã¾ããã¾ããã以å¤ã«ãç¹å®ã®å½¢å¼ã§è¨è¿°ããããã¼ã¿ããã¼ã¹ãã¦åãåºããã¨ãã§ãã¾ããä¾ãã°æ¬¡ã®ãããªHTMLãã©ã¼ã ããPOSTã®ãªã¯ã¨ã¹ããéãã±ã¼ã¹ãèãã¦ã¿ã¾ãã
<form method="post" action="/accounts/"> {% csrf_token %} <input type="text" name="username"> <input type="text" name="email"> <button type="submit">æ稿</button> </form>
ããã®HTMLãã©ã¼ã ã¯usernameãemailã®æååã/accountsã«éä¿¡ãã¾ããå®éã«HTTPã®ãªã¯ã¨ã¹ãã¯æ¬¡ã®ããã«ãªã£ã¦ãã¾ãã
POST /accounts/ HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Content-Length: 44 username=shibata&email=shibata%40example.com
ãusernameã¨emailã®æ å ±ãã¡ãã»ã¼ã¸ããã£ã¼å ã«è¨è¿°ããã¦ãã¾ãããã®å½¢å¼ã¯GETãªã¯ã¨ã¹ãæã«URLã«å«ããã¯ã¨ãªæååã®å½¢å¼ã¨åãã§ããã©ã¡ã¼ã¿ã¼ã¯&ã«ãã£ã¦åºåãããkeyã¨valueã¯ã=ã«ãã£ã¦åºåããã¾ããã¾ããè±æ°å以å¤ã¯ãã¼ã»ã³ãã¨ã³ã³ã¼ãããããããã@ã¯%40ã«å¤ãã£ã¦ãã¾ãããã®ãããªå½¢å¼ãx-www-form-urlencodedå½¢å¼ã¨å¼ã³ã¾ããHttpRequestãªãã¸ã§ã¯ãã®POSTå±æ§ã¯ããã®x-www-form-urlencodedå½¢å¼ã®ãã¼ã¿ããã¼ã¹ããQueryDictã¨ããè¾æ¸ã©ã¤ã¯ãªãªãã¸ã§ã¯ãã§æä¾ãã¦ããã¾ããform-urlencodedã®å½¢å¼ã¯ãkeyã®éè¤ã許容ãããã1ã¤ã®keyã«å¯¾ãã¦è¤æ°ã®å¤ããã¡ã¾ãã
>>> from django.http import QueryDict >>> QueryDict("user=shibata&user=masashi&email=shibata%40example.com") <QueryDict: {'user': ['shibata', 'masashi'], 'email': ['[email protected]']}>
multipart/form-dataå½¢å¼ã®ãã¼ã¿ã®èªã¿è¾¼ã¿
ã<form>ã¿ã°ã®enctypeå±æ§ã¾ãã¯<button>ã<input>ã®formenctypeå±æ§ãæå®ããã¨ã¡ãã»ã¼ã¸ããã£ã¼ã®å½¢å¼ãå¤ããã¾ããå ç¨ç´¹ä»ããx-www-form-urlencoded以å¤ã«ããmultipart/form-dataãtext/plainãæå®ã§ãã¾ããããã§ã¯multipart/form-dataãæå®ãã¦ã¿ã¾ãããã
<form method="post" action="/accounts/" enctype="multipart/form-data"> {% csrf_token %} <input type="text" name="username"> <input type="image" name="icon_url"> <button type="submit">{% trans "Post" %}</button> </form>
ããã®ãã©ã¼ã ã®icon_urlãã£ã¼ã«ãã§ã¯ãç»åãã¡ã¤ã«ãé¸æãã¾ãã
ãmultipart/form-dataã¯ããã®ããã«HTMLãã©ã¼ã ãããã¡ã¤ã«ãã¢ãããã¼ãããéã«å¿ è¦ã«ãªãã¾ããã¡ãã»ã¼ã¸ããã£ã¼ãã©ã®ããã«ãªã£ã¦ããã確èªãã¦ã¿ã¾ãããã
POST /accounts/ HTTP/1.1 Host: example.com Content-Type: multipart/form-data;boundary="boundary" --boundary Content-Disposition: form-data; name="username" shibata --boundary Content-Disposition: form-data; name="icon_url"; filename="profile.png" Content-Transfer-Encoding: binary contents of profile.png... --boundary--
ãContent-Typeãããã¼ã§æå®ãããåºåãæååã«ãã£ã¦ã¡ãã»ã¼ã¸ããã£ã¼ãåå²ããã¦ãã¾ãããã®ä¸ã«ããã«ãããã¼ãåå¨ãã空è¡ã空ãã¦ç»åãã¼ã¿ã®ä¸èº«ãè¨è¿°ããã¦ãã¾ãã
ãmultipart/form-dataå½¢å¼ã®ãªã¯ã¨ã¹ãã«ã¯ãHttpRequestãªãã¸ã§ã¯ãã®FILESå±æ§ããã¢ã¯ã»ã¹ã§ãã¾ãããã¡ã¤ã«ã¢ãããã¼ããªã©ãå®è£ ããéã¯ãã®å 容ãæ¼ããã¦ããã¨ããã§ãããã
conditionãã³ã¬ã¼ã¿ã¼ã使ã£ãETagãLast-Modifiedãããã¼ã®å¶å¾¡
ãHTTPã«ã¯ãã¯ã©ã¤ã¢ã³ãã«ã¬ã¹ãã³ã¹å 容ããã£ãã·ã¥ãããä»çµã¿ãããã¤ãããã¾ããããã¯ã©ã¤ã¢ã³ããæå¾ã«ã¢ã¯ã»ã¹ãã¦ãããã¾ã ãµã¼ãã¼ã®ã¬ã¹ãã³ã¹å 容ã«å¤åããªããã¨ãããã£ã¦ããã°ãã¬ã¹ãã³ã¹ã®ã¡ãã»ã¼ã¸ããã£ã¼ã¯ç©ºã§ãåé¡ãªãã¯ãã§ããããã§ã¯ã©ã¤ã¢ã³ããæ¢ã«ææ°ã®ã³ã³ãã³ãããã£ãã·ã¥ãã¦ãããã¨ãããã£ã¦ããå ´åã«ã¯ããµã¼ãã¼ã¯304 Not Modifiedãè¿ãã¾ãã
ããã¦ãããã§ã¯ã©ã®ããã«ãã¦ã¯ã©ã¤ã¢ã³ããæ¢ã«ææ°ã®ã³ã³ãã³ãããã£ãã·ã¥ãã¦ããããå¤æããã®ã§ããããã1ã¤ã®æ¹æ³ã¯æ¬¡ã®ãããªLast-Modifiedãããã¼ã«ããå¶å¾¡ã§ãã
Last-Modified: Mon, 22 Mar 2021 01:08:18 GMT
ãLast-Modifiedãããã¼ã®å¤ã«ã¯ãHTTP-dateã¿ã¤ã ã¹ã¿ã³ãå½¢å¼ã§æå¾ã«ã³ã³ãã³ããæ´æ°ãããæ¥æãæå®ãã¾ããã¯ã©ã¤ã¢ã³ãå´ã¯ãã³ã³ãã³ãã«ã¢ã¯ã»ã¹ããéã®Last-Modifiedãããã¼ã®å¤ãè¨æ¶ãã次å以éã®ã¢ã¯ã»ã¹ã®éã«æ¬¡ã®ãããªIf-Modified-Sinceãããã¼ããªã¯ã¨ã¹ããããã¼ã«å«ãã¾ãã
If-Modified-Since: Mon, 22 Mar 2021 01:08:18 GMT
ãããã¨ãµã¼ãã¼ã¯ãç¾å¨ã®ã³ã³ãã³ãã®æçµæ´æ°æ¥æã¨If-Modified-Sinceãããã¼ã®æ¥æãæ¯è¼ãã¾ããããã³ã³ãã³ãã®æçµæ´æ°æ¥æã®æ¹ãæ°ãããã°200 OKãè¿ããå¤ããã°304 Not Modifiedãè¿ãã¾ãã304 Not Modifiedãè¿ãéã«ã¯ãHTTPã¬ã¹ãã³ã¹ã®ã¡ãã»ã¼ã¸ããã£ã¼ã«ã¯ä½ãå ¥ã£ã¦ãã¾ãããDjangoã§ã¯conditionãã³ã¬ã¼ã¿ã¼ãç¨ãã¦æ¬¡ã®ããã«è¨è¿°ã§ãã¾ãã
from django.views.decorators.http import condition def last_modified_func(request, snippet_id): try: snippet = Snippet.objects.get(id=snippet_id) except Snippet.DoesNotExist: return None # ã³ã³ãã³ããåå¨ããªããã°Noneãè¿ãã return snippet.updated_at @condition(last_modified_func=last_modified_func) def my_view(request, snippet_id): ...
ãlast_modified_funcã¯ããã¥ã¼é¢æ°ã¨åãå¼æ°ãåãåã£ã¦ãæçµæ´æ°æ¥æã示ãdatetimeãªãã¸ã§ã¯ããè¿ãé¢æ°ã§ããããã³ã³ãã³ããããããåå¨ããªããã°ãNoneãè¿ãã¾ãã
ãLast-Modifiedãããã¼ã¯æ¥æã«ãããã£ãã·ã¥ãå¶å¾¡ãã¾ãããæ¥æã®æ¯è¼ã§ã¯ãã£ãã·ã¥ã®æå¹æ§ã表ç¾ã§ããªããã¨ããã°ãã°ããã¾ãããã®ãããªã±ã¼ã¹ã§ã¯ãªã¯ã¨ã¹ãå 容ãã³ã³ãã³ãã®æ å ±ãããä½ããã®ããã·ã¥å¤ãè¨ç®ãããã®ããã·ã¥å¤ãä¸è´ãããã©ãããè¦ãæ¹æ³ãããã¾ãã
ããããå®ç¾ããã®ãETagãããã¼ã§ãã大ã¾ããªæµãã¯Last-Modifiedãããã¼ã®ã¨ãã¨å¤ããã¾ããããµã¼ãã¼ã¯ãã³ã³ãã³ããè¿ãéã«HTTPã®ã¬ã¹ãã³ã¹ãããã¼ã«ETag:
ãã¾ãdjango.views.decorators.httpã¢ã¸ã¥ã¼ã«ã¯ãetagãã³ã¬ã¼ã¿ã¼ãlast_modifiedãã³ã¬ã¼ã¿ã¼ãæä¾ãã¾ãããããã¯æ¬¡ã®ãããªãã ã®ã·ã§ã¼ãã«ããé¢æ°ã§ãããã®ããconditionãã³ã¬ã¼ã¿ã¼ãç解ã§ãã¦ããã°ååã§ãã
def etag(etag_func): return condition(etag_func=etag_func) def last_modified(last_modified_func): return condition(last_modified_func=last_modified_func)

å®è·µDjango
Pythonã«ããæ¬æ ¼Webã¢ããªã±ã¼ã·ã§ã³éçº
èè
ï¼èç°å°
çºå£²æ¥ï¼2021å¹´7æ19æ¥ï¼æï¼
å®ä¾¡ï¼3,850åï¼æ¬ä½3,500åï¼ç¨10%ï¼
Djangoã®å®è·µçãªãã¹ããã¯ããã¯ãã¦ã¼ã¶ã¼ã¢ãã«ã®ã«ã¹ã¿ãã¤ãºæ¹æ³ãèªè¨¼å¦çã®ãã¹ããã©ã¯ãã£ã¹ãªã©ãWebéçºã«ããã¦å¿ ãç¥ã£ã¦ããã¹ãå 容ãå¹ åºãåãä¸ãã¾ããã