教程2:請求和響應
從這一點來說堤舒,我們將真正開始覆蓋REST框架的核心。我們來介紹一些基本的構建塊誉碴。
請求對象
REST框架引入了一個Request
擴展常規(guī)的對象HttpRequest
隆圆,并提供更靈活的請求解析。Request
對象的核心功能是與Web API request.data
類似的屬性request.POST
翔烁,但更為有用渺氧。
request.POST # Only handles form data. Only works for 'POST' method.
request.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.
響應對象
REST框架還引入了一個Response
對象,它是一種類型的對象蹬屹,它TemplateResponse
使用未呈現(xiàn)的內容并使用內容協(xié)商來確定返回給客戶端的正確內容類型侣背。
return Response(data) # Renders to content type as requested by the client.
狀態(tài)碼
在視圖中使用數(shù)字HTTP狀態(tài)代碼并不總是顯而易見的閱讀,并且如果錯誤代碼錯誤慨默,很容易不注意贩耐。REST框架為每個狀態(tài)代碼提供更明確的標識符,例如HTTP_400_BAD_REQUEST
在status
模塊中厦取。這是一個好主意潮太,而不是使用數(shù)字標識符。
包裝API視圖
REST框架提供了兩個可用于編寫API視圖的包裝器。
-
@api_view
用于處理基于函數(shù)的視圖的裝飾器铡买。 - 該
APIView
班與基于類的視圖工作更鲁。
這些包裝提供了一些功能,例如確保Request
在視圖中接收實例奇钞,并向Response
對象添加上下文澡为,以便可以執(zhí)行內容協(xié)商。
這些包裝器還提供了一些行為景埃,例如405 Method Not Allowed
在適當時返回響應媒至,以及處理ParseError
在request.data
格式錯誤的輸入中訪問時發(fā)生的任何異常。
把它們結合在一起
好的谷徙,讓我們繼續(xù)拒啰,開始使用這些新的組件來寫幾個意見。
我們不再需要我們的JSONResponse
類完慧,在views.py
中谋旦,所以繼續(xù)刪除。一旦完成骗随,我們可以開始重構我們的視圖。
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
我們的實例視圖是對前一個示例的改進赴叹。它更簡潔一些鸿染,現(xiàn)在的代碼感覺和我們使用Forms API非常相似。我們還使用了指定的狀態(tài)碼乞巧,這使得響應的含義更加明顯涨椒。
以下是views.py
模塊中單個片段的視圖。
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
這應該都感到非常熟悉 - 這與使用常規(guī)Django視圖沒有多大區(qū)別绽媒。
請注意蚕冬,我們不再明確地將我們的請求或響應綁定到給定的內容類型。 request.data
可以處理傳入的json
請求是辕,但也可以處理其他格式囤热。同樣,我們使用數(shù)據(jù)返回響應對象获三,但允許REST框架將響應呈現(xiàn)給我們正確的內容類型旁蔼。
添加可選的格式后綴到我們的網(wǎng)址
為了利用我們的響應不再被硬連接到單個內容類型的事實,讓我們添加對格式后綴的支持到我們的API端點疙教。使用格式后綴給了我們明確引用給定格式的URL棺聊,并且意味著我們的API將能夠處理諸如http://example.com/api/items/4.json之類的 URL 。
通過向format
兩個視圖添加關鍵字參數(shù)開始贞谓,就像這樣限佩。
def snippet_list(request, format=None):
和
def snippet_detail(request, pk, format=None):
現(xiàn)在snippets/urls.py
稍微更新文件,以附加一組format_suffix_patterns
現(xiàn)有的URL。
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.snippet_list),
url(r'^snippets/(?P<pk>[0-9]+)$', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
我們不一定需要添加這些額外的url模式祟同,但它給了我們一個簡單作喘,干凈的方式來引用一個特定的格式。
它看起來如何耐亏?
繼續(xù)從命令行測試API徊都,就像我們在教程第一部分中所做的那樣。一切工作都非常類似广辰,盡管如果我們發(fā)送無效的請求暇矫,我們已經(jīng)有了更好的錯誤處理。
和以前一樣择吊,我們可以得到所有片段的列表李根。
http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
...
[
{
"id": 1,
"title": "",
"code": "foo = \"bar\"\n",
"linenos": false,
"language": "python",
"style": "friendly"
},
{
"id": 2,
"title": "",
"code": "print \"hello, world\"\n",
"linenos": false,
"language": "python",
"style": "friendly"
}
]
我們可以通過使用Accept
頭來控制返回的響應的格式:
http http://127.0.0.1:8000/snippets/ Accept:application/json # Request JSON
http http://127.0.0.1:8000/snippets/ Accept:text/html # Request HTML
或者通過附加格式后綴:
http http://127.0.0.1:8000/snippets.json # JSON suffix
http http://127.0.0.1:8000/snippets.api # Browsable API suffix
同樣几睛,我們可以使用Content-Type
標題來控制我們發(fā)送的請求的格式房轿。
# POST using form data
http --form POST http://127.0.0.1:8000/snippets/ code="print 123"
{
"id": 3,
"title": "",
"code": "print 123",
"linenos": false,
"language": "python",
"style": "friendly"
}
# POST using JSON
http --json POST http://127.0.0.1:8000/snippets/ code="print 456"
{
"id": 4,
"title": "",
"code": "print 456",
"linenos": false,
"language": "python",
"style": "friendly"
}
如果您將--debug
開關添加到上述http
請求中,您將能夠在請求標頭中看到請求類型所森。
(blog) [root@centos7-01 django_auth_example]# http --form --debug POST http://192.168.152.131:5000/snippets/ code="import sys"
HTTPie 0.9.9
Requests 2.18.4
Pygments 2.2.0
Python 3.6.2 (default, Sep 12 2017, 18:57:38)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]
/root/.pyenv/versions/3.6.2/envs/blog/bin/python
Linux 3.10.0-693.2.2.el7.x86_64
<Environment {
"colors": 8,
"config": {
"__meta__": {
"about": "HTTPie configuration file",
"help": "https://httpie.org/docs#config",
"httpie": "0.9.9"
},
"default_options": "[]"
},
"config_dir": "/root/.httpie",
"is_windows": false,
"stderr": "<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>",
"stderr_isatty": true,
"stdin": "<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>",
"stdin_encoding": "UTF-8",
"stdin_isatty": true,
"stdout": "<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>",
"stdout_encoding": "UTF-8",
"stdout_isatty": true
}>
>>> requests.request(**{
"allow_redirects": false,
"auth": "None",
"cert": "None",
"data": {
"code": "import sys"
},
"files": {},
"headers": {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"User-Agent": "HTTPie/0.9.9"
},
"method": "post",
"params": {},
"proxies": {},
"stream": true,
"timeout": 30,
"url": "http://192.168.152.131:5000/snippets/",
"verify": true
})
HTTP/1.0 201 Created
Allow: OPTIONS, POST, GET
Content-Type: application/json
Date: Tue, 30 Jan 2018 09:14:17 GMT
Server: WSGIServer/0.2 CPython/3.6.2
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
{
"code": "import sys",
"id": 5,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
}
現(xiàn)在囱持,通過訪問http://127.0.0.1:8000/snippets/,在Web瀏覽器中打開API 焕济。
瀏覽功能
由于API根據(jù)客戶端請求選擇響應的內容類型纷妆,因此默認情況下,當Web瀏覽器請求資源時晴弃,將返回HTML格式的資源表示形式掩幢。這允許API返回完全的可瀏覽網(wǎng)頁的HTML表示。
擁有一個可瀏覽網(wǎng)頁的API是一個巨大的可用性的勝利上鞠,使開發(fā)和使用您的API更容易际邻。它也大大降低了想要檢查和使用您的API的其他開發(fā)人員的進入門檻。
有關可瀏覽的API功能以及如何對其進行定制的更多信息芍阎,請參閱可瀏覽的API主題世曾。
下一步是什么?
在教程第3部分中谴咸,我們將開始使用基于類的視圖度硝,并查看通用視圖如何減少我們需要編寫的代碼量。