1. 程式人生 > >2 - Requests and responses

2 - Requests and responses

教程 2:請求和響應

從現在開始,我們將真正開始接觸 REST framework 的核心。 我們來介紹幾個基本的構建模組。

請求物件 (request objects)

REST framework 引入了一個擴充套件了常規 HttpRequestRequest 物件,並提供了更靈活的請求解析。Request 物件的核心功能是 request.data 屬性,它與 request.POST 類似,但對於使用 Web API 更加有用。

request.POST # 只處理表單資料。 只適用於 'POST' 方法。
request.data # 處理任意資料。 適用於 'POST','PUT' 和 'PATCH' 方法。

響應物件 (Response objects)

REST framework 還引入了一個 Response 物件,該物件是一種獲取未渲染內容的 TemplateResponse 型別,並使用內容協商來確定正確內容型別返回給客戶端。

return Response(data) # 渲染成客戶端請求的內容型別。

狀態碼 (Status codes)

在您的檢視中使用數字 HTTP 狀態碼並不總是利於程式碼的閱讀,如果寫錯程式碼,很容易被忽略。REST framework 為每個狀態碼提供更明確的識別符號,譬如 status 模組中的 HTTP_400_BAD_REQUEST

。使用它們是一個好主意,而不是使用數字識別符號。

包裝 API 檢視 (Wrapping API views)

REST framework 提供了兩種編寫 API 檢視的封裝。

  • 1.用於基於函式檢視的 @api_view 裝飾器。
  • 2.用於基於類檢視的 APIView 類。

這些檢視封裝提供了一些功能,例如確保你的檢視能夠接收 Request 例項,並將上下文新增到 Response 物件,使得內容協商可以正常的運作。

檢視封裝還內建了一些行為,例如在適當的時候返回 405 Method Not Allowed 響應,並處理訪問錯誤的輸入 request.data

時觸發的任何 ParseError 異常。

組合在一起

好的,讓我們開始使用這些新的元件來寫幾個檢視。

我們不再需要 views.pyJSONResponse 類,所以把它刪除掉。然後,我們可以重構我們的檢視。

@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    列出所有的程式碼 snippet,或建立一個新的 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)

我們的例項檢視比前面的示例有所改進。它稍微簡潔一點,現在的程式碼與我們使用 Forms API 時非常相似。我們還使用了指定的狀態碼,這使得響應更加明顯。

以下是 views.py 模組中單個 snippet 的檢視。

@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    """
    獲取,更新或刪除一個程式碼 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)

這對我們來說應該都是非常熟悉的 - 它和正常 Django 檢視並沒有什麼不同。

注意,我們不再顯式地將請求或響應繫結到給定的內容型別。request.data 可以處理傳入的 json 請求,但它也可以處理其他格式。同樣,我們返回帶有資料的響應物件,但允許 REST framework 將響應渲染成正確的內容型別。

為我們的網址新增可選的格式字尾

為了利用我們的響應不再被硬連線到單個內容型別的事實,讓我們將格式字尾的支援新增到我們的API端點。使用格式字尾,為我們提供了明確指向給定格式的 URL,這意味著我們的 API 可以處理一些 URLs,類似這樣的格式 http://example.com/api/items/4.json。

像下面這樣在這兩個檢視中新增一個 format 關鍵字引數。

def snippet_list(request, format=None):

def snippet_detail(request, pk, format=None):

現在更新 snippet/urls.py 檔案,在現有的 urls 基礎上追加一組 format_suffix_patterns

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 模式,但它給了我們一個簡單,清晰的方式來引用特定的格式。

它看起來如何?

繼續從命令列測試 API,就像我們在教程第一部分所做的那樣。一切都非常類似,儘管我們對一些無效的請求有更好的錯誤處理。

我們可以像以前一樣獲取所有 snippet 的列表。

(env) [email protected]:~/django_rest_framework/tutorial$ http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
Allow: POST, OPTIONS, GET
Content-Length: 317
Content-Type: application/json
Date: Thu, 14 Jun 2018 15:49:40 GMT
Server: WSGIServer/0.2 CPython/3.5.2
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

[
    {
        "code": "foo = \"bar\"\n",
        "id": 1,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"\n",
        "id": 2,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"",
        "id": 3,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    }
]

我們可以通過使用 Accept 請求頭,來控制我們返回的響應的格式。

http http://127.0.0.1:8000/snippets/ Accept:application/json  # 請求 JSON
http http://127.0.0.1:8000/snippets/ Accept:text/html         # 請求 HTML

或者通過追加格式字尾:

http http://127.0.0.1:8000/snippets.json  # JSON 字尾
http http://127.0.0.1:8000/snippets.api   # 可瀏覽 API 字尾

類似的,我們可以通過 Content-Type 請求頭來控制我們傳送請求的格式。

# POST 表單資料
(env) [email protected]:~/django_rest_framework/tutorial$ http --form POST http://127.0.0.1:8000/snippets/ code="print 123"
HTTP/1.1 201 Created
Allow: POST, OPTIONS, GET
Content-Length: 93
Content-Type: application/json
Date: Thu, 14 Jun 2018 16:00:37 GMT
Server: WSGIServer/0.2 CPython/3.5.2
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "code": "print 123",
    "id": 4,
    "language": "python",
    "linenos": false,
    "style": "friendly",
    "title": ""
}

# POST JSON 資料
(env) [email protected]:~/django_rest_framework/tutorial$ http --json POST http://127.0.0.1:8000/snippets/ code="print 456"
HTTP/1.1 201 Created
Allow: POST, OPTIONS, GET
Content-Length: 93
Content-Type: application/json
Date: Thu, 14 Jun 2018 16:02:24 GMT
Server: WSGIServer/0.2 CPython/3.5.2
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "code": "print 456",
    "id": 5,
    "language": "python",
    "linenos": false,
    "style": "friendly",
    "title": ""
}

如果你向上述 http 請求中新增 --debug ,則可以在請求頭中檢視請求型別。

現在可以在瀏覽器中訪問 http://127.0.0.1:8000/snippets/ 檢視 API 。

視覺化

由於 API 是基於客戶端發起的請求來選擇響應內容的格式,因此,當接收到來自瀏覽器的請求時,會預設以 HTML 格式來描述資料。這允許 API 返回網頁完全可瀏覽的 HTML。

擁有可瀏覽的網頁 API 是一個巨大的勝利,並且使開發和使用 API 更加容易。它也大大降低了其他開發人員檢查和使用 API 的門檻。

有關可瀏覽的 API 功能以及如何對其進行定製的更多資訊,請參閱可瀏覽的 API主題。

下一步是什麼?

教程的第三部分,我們將開始使用基於類的檢視,並檢視通用檢視如何減少我們需要編寫的程式碼量。