歡迎訪問我的個人網站:www.comingnext.cn
前言
在上一篇文章,已經實現了訪問指定URL就返回了指定的數據,這也體現了RESTful API的一個理念,每一個URL代表着一個資源。當然我們還知道RESTful API的另一個特性就是,發送不同的請求動作,會返還不同的響應,這篇文章就講一下django-rest-framework這個工具在這方面給我們帶來的便捷操作。
Request對象
平時我們在寫Django的視圖函數的時候,都會帶上一個request參數,這樣就能處理平時搭建網站時,瀏覽器訪問網頁時發出的常規的HttpRequest。但是現在我們導入了django-rest-framework,它能夠對request進行拓展,並且提供更靈活的請求解析。這個特性體現在哪呢?請看下面這個例子:
request.POST # 只能處理表單數據.只能處理POST請求 request.data # 能處理各種數據。 可以處理'POST', 'PUT' 和 'PATCH'模式的請求
這個例子里面的注釋已經說得很清楚,拓展后的request使用request.data就可以處理各種各樣的請求了,而原本的request在處理時需要指定請求模式。
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對象就很方便了,它會根據客戶端的請求頭部信息來確定正確的內容類型以返回給客戶端。只需如下代碼:
return Response(data)
稍后會改進上一篇文章的程序對此進一步的具體講解。
狀態碼
我們知道發送http請求時會返回各種各樣的狀態嗎,但是都是簡單的數字,比如200、404等,這些純數字標識符有時候可能不夠明確或者客戶端在使用的時候不清楚錯誤信息甚至是沒注意看不到,所以django-rest-framework也對此進行了優化,狀態碼會是HTTP_400_BAD_REQUEST、HTTP_404_NOT_FOUND這種,極大的提高可讀性。
裝飾API視圖
REST框架還提供了一個裝飾器和一個類來包裝視圖函數,可以使用它們來寫API視圖,讓程序能處理的情況更多。
- @api_view裝飾器用在基於視圖的方法上。
- APIView類用在基於視圖的類上。
注意:本文使用的是基於視圖方法,所以使用的是裝飾器@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)
可以看出,經過改進的代碼已經把上面所說的幾個django-rest-framework帶來的特性都應用起來了,我們可以看出程序代碼量變少,並且能處理的情況更多了。 比如說,在原本的視圖函數snippet_detail中,處理'PUT'請求的時候,需要先解析json格式的數據再進一步處理:
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
也就是說需要分成兩步實現,而且這里有一個限制就是只能解析json格式的數據流。而改進后的程序只需一行代碼:
serializer = SnippetSerializer(data=request.data)
直接使用之前說的request.data就可以獲取到提交過來的數據了,並且可以處理各種數據和各種請求動作,方便了開發。
還有在return的時候也不需要指定json格式了,由原本的
return JsonResponse(serializer.data, status=201)
改成了
return Response(serializer.data,status=status.HTTP_201_CREATED)
這也意味着返回給客戶端的可以是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)
以上就是對原有的常規的Django視圖函數的改進。
總結一下就是處理request提交過來的數據不需要一定是json格式的數據,返回的響應也不需要一定是json數據,也可以是經過渲染的HTML頁面。稍后就會示范使用。
向URL添加可選的格式后綴
既然上面已經說了返回給客戶端的Response可是json或者是HTML等格式的內容,那么用戶在使用的時候是如何指定返回哪種格式的內容呢,那就是在URL的最后加上后綴。比如http://127.0.0.1:8000/snippets.json,這樣就是用戶自己指定了返回json格式的Response,而不是我們在后台指定返回固定的格式。
只需對我們的程序稍加改進就可以了,在兩個視圖函數添加關鍵詞參數format:
def snippet_list(request, format=None):
以及
def snippet_detail(request, pk, format=None):
再修改一下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)
改進后的使用
首先當然還是可以像上一篇文章中那樣的使用:
也可以通過設置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
效果如下(返回的是頁面的HTML代碼,只展示了一部分):
還可以直接加格式后綴:
http http://127.0.0.1:8000/snippets.json # JSON suffix http http://127.0.0.1:8000/snippets.api # Browsable API suffix
當然啦,在命令行查看HTML代碼就沒啥意思了,我們可以直接在瀏覽器輸入 http://127.0.0.1:8000/snippets.api 進行查看,會得到一個美觀的頁面:
如果我們要增添數據怎么辦?我們可以控制 Content-Type 頭部信息來提交POST請求:
http --form POST http://127.0.0.1:8000/snippets/ code="print 123" http --json POST http://127.0.0.1:8000/snippets/ code="print 456"
它會自動在原有的數據后面添加你提交過去的數據,效果如下:
上面說了,改進后可以處理錯誤的提交,比如把code改成了codes,就會給出錯誤信息:
圖中給出的錯誤信息是 400 Bad Request,這和我們在視圖函數中定義的是一樣的:
return Response(serializer.data,status=status.HTTP_400_BAD_REQUEST)
在請求中如果加入了--debug可以查看到詳細的請求信息和類型:
在上面介紹@api_view和APIView的時候,提到了在適當的時候返回405 Method Not Allowed狀態碼。這個所謂適當的時候就要回看到剛才寫視圖函數的時候,修飾器的代碼:
@api_view(['GET','POST'])
以及
@api_view(['GET','PUT','DELETE'])
這兩行代碼就規定了在調用這兩個函數,也就是訪問到相關的URL時,只能使用指定的請求動作,否則就會報出405 Method Not Allowed錯誤。例如訪問 http://127.0.0.1:8000/snippets.json 時用了PUT請求就會報這個錯:
正確的更改數據應該如下:
http --json PUT http://127.0.0.1:8000/snippets/1.json code="hello world"
這樣就把 id=1 的數據修改了。想要刪除也是一樣的:
這樣就可以把 id=3 的數據刪除掉了。
OK,關於Django RESTful API的請求和響應部分的處理就先講到這了。下一篇會介紹基於類的視圖,多謝支持~
本文地址:http://www.cnblogs.com/zivwong/p/7427394.html
作者博客:ziv
歡迎轉載,請在明顯位置給出出處及鏈接