天天看點

Django RESTful API (2) 請求和響應

歡迎關注個人微信公衆号,大大大碗面,不定期分享AI論文解讀和開發技術,網際網路小白,輕噴~

Django RESTful API (2) 請求和響應

Request對象

平時我們在寫Django的視圖函數的時候,都會帶上一個request參數,這樣就能處理平時搭建網站時,浏覽器通路網頁時發出的正常的HttpRequest。但是現在我們導入了django-rest-framework,它能夠對request進行拓展,并且提供更靈活的請求解析。這個特性展現在哪呢?請看下面這個例子:

request.POST  # 隻能處理表單資料.隻能處理POST請求
request.data  # 能處理各種資料。  可以處理'POST', 'PUT' 和 'PATCH'模式的請求
           

Response對象

和request對象一樣,django-rest-framework也對其進行了很實用的拓展,在我上一篇文章的snippets/views.py中,我們導入了JsonResponse用于傳回json格式的響應,在視圖函數中是這樣的:

@csrf_exempt
def snippet_list(request):
    '''
    列出所有已經存在的snippet或者建立一個新的snippet
    '''
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)
           

也就是說,在return的時候就需要指明json格式,這樣顯得很不實用而且很單一,是以經過拓展後的Reponse對象就很友善了,它會根據用戶端的請求頭部資訊來确定正确的内容類型以傳回給用戶端。隻需如下代碼:

狀态碼

我們知道發送http請求時會傳回各種各樣的狀态嗎,但是都是簡單的數字,比如200、404等,這些純數字辨別符有時候可能不夠明确或者用戶端在使用的時候不清楚錯誤資訊甚至是沒注意看不到,是以django-rest-framework也對此進行了優化,狀态碼會是HTTP_400_BAD_REQUEST、HTTP_404_NOT_FOUND這種,極大的提高可讀性。

裝飾API視圖

REST架構還提供了一個裝飾器和一個類來包裝視圖函數,可以使用它們來寫API視圖,讓程式能處理的情況更多。

  • @api_view裝飾器用在基于視圖的++方法++上。
  • APIView類用在基于視圖的++類++上。

這兩個東西提供的一些功能,讓我們省去很多工作,比如說確定你在視圖中收到Request對象或在你的Response對象中添加上下文,這樣就能實作内容通信。

另外裝飾器可以在接收到輸入錯誤的request.data時抛出ParseError異常,或者在适當的時候傳回405 Method Not Allowed狀态碼。

應用

上面說了這麼多拓展和優化,接下來就把它們都使用起來,改進一下原本的snippets/views.py,程式如下:

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):
    """
    列出所有已經存在的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)
           

改變一:

在原本的視圖函數snippet_detail中,處理’PUT’請求的時候,需要先解析json格式的資料再進一步處理:

data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
           

也就是說需要分成兩步實作,而且這裡有一個限制就是隻能解析json格式的資料流。而改進後的程式隻需一行代碼:

直接使用之前說的request.data就可以擷取到送出過來的資料了,并且可以處理各種資料和各種請求動作,友善了開發。

改變二:

return的時候也不需要指定json格式了,由原本的:

改成了:

這也意味着傳回給用戶端的可以是json或者html等格式的内容,傳回HTML格式的内容的話,會在浏覽器傳回經過渲染的、更美觀的頁面。同時可以看出狀态碼也改進成了django-rest-framework給我們帶來的可讀性更高的狀态辨別碼,以上這些措施都很大程度的提高了對客戶的友好度。

對于另一個視圖函數的修改也是同樣的原理,這裡就不做同樣的講解了,代碼直接貼出:

@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a snippet instance.
    """
    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)
           

總結一下就是處理request送出過來的資料不需要一定是json格式的資料,傳回的響應也不需要一定是json資料,也可以是經過渲染的HTML頁面。稍後就會示範使用。

向URL添加可選的格式字尾

既然上面已經說了傳回給用戶端的Response可是json或者是HTML等格式的内容,那麼使用者在使用的時候是如何指定傳回哪種格式的内容呢,那就是在URL的最後加上字尾。比如http://127.0.0.1:8000/snippets.json,這樣就是使用者自己指定了傳回json格式的Response,而不是我們在背景指定傳回固定的格式。

隻需對我們的程式稍加改進就可以了,在兩個視圖函數添加關鍵詞參數format:

以及:

再修改一下snippets/urls.py,導入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)   # 導入格式字尾模式
           

改進後的使用

上一節所述的使用方法:

Django RESTful API (2) 請求和響應

也可以通過設定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
           

效果如下:

Django RESTful API (2) 請求和響應

我們可以直接在浏覽器輸入 http://127.0.0.1:8000/snippets.api 進行檢視,會得到一個美觀的頁面:

Django RESTful API (2) 請求和響應

如果我們需要添加資料,那麼我們可以控制 Content-Type 頭部資訊來送出POST請求:

Django RESTful API (2) 請求和響應

上面說了,改進後可以處理錯誤的送出,比如把code改成了codes,就會給出錯誤資訊:

Django RESTful API (2) 請求和響應

在請求中如果加入了–debug可以檢視到詳細的請求資訊和類型:

Django RESTful API (2) 請求和響應

在上面介紹@api_view和APIView的時候,提到了在适當的時候傳回405 Method Not Allowed狀态碼。這個所謂适當的時候就要回看到剛才寫視圖函數的時候,修飾器的代碼:

@api_view(['GET','POST'])
@api_view(['GET','PUT','DELETE'])
           

這兩行代碼就規定了在調用這兩個函數,也就是通路到相關的URL時,隻能使用指定的請求動作,否則就會報出405 Method Not Allowed錯誤

正确的使用方式:

http --json PUT http://127.0.0.1:8000/snippets/1.json code="hello wo
rld"
           
Django RESTful API (2) 請求和響應

這樣就把id=1的資料修改了。想要删除也是一樣的:

Django RESTful API (2) 請求和響應