前言:在上一篇文章,已經實現了訪問指定URL就返回了指定的數據,這也體現了RESTful API的一個理念,每個URL表明着一個資源。固然咱們還知道RESTful API的另外一個特性就是,發送不一樣的請求動做,會返還不一樣的響應,這篇文章就講一下django-rest-framework這個工具在這方面給咱們帶來的便捷操做。html
平時咱們在寫Django的視圖函數的時候,都會帶上一個request參數,這樣就能處理平時搭建網站時,瀏覽器訪問網頁時發出的常規的HttpRequest。可是如今咱們導入了django-rest-framework,它可以對request進行拓展,而且提供更靈活的請求解析。這個特性體如今哪呢?請看下面這個例子:前端
request.POST # Only handles form data. Only works for 'POST' method.
request.data # Handles arbitrary(任意的) data. Works for 'POST', 'PUT' and 'PATCH' methods.
request.POST只能處理前端發起的POST請求,只能處理表單提交的數據。而request.data能夠處理任意數據,而不僅僅是前端提交的表單數據,可用於post, put, patch請求。python
和request對象同樣,django-rest-framework也對其進行了很實用的拓展,在我上一篇文章的snippets/views.py中,咱們導入了JsonResponse用於返回json格式的響應,在視圖函數中是這樣的:web
@csrf_exempt def snippet_list(request): """ because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as csrf_exempt List all code snippets, or create a new 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對象就很方便了,它會根據客戶端的請求頭部信息來肯定正確的內容類型以返回給客戶端。只需以下代碼:django
return Response(data) # Renders to content type as requested by the client.
咱們知道發送http請求時會返回各類各樣的狀態嗎,可是都是簡單的數字,好比200、404等,這些純數字標識符有時候可能不夠明確或者客戶端在使用的時候不清楚錯誤信息甚至是沒注意看不到,因此django-rest-framework也對此進行了優化,狀態碼會是HTTP_400_BAD_REQUEST、HTTP_404_NOT_FOUND這種,極大的提升可讀性json
REST框架提供了兩個可用於編寫API視圖的包裝器。api
@api_view裝飾器
用於處理基於函數的視圖這些包裝提供了一些功能,讓咱們省去不少工做。好比說確保你在視圖中收到Request對象或在你的Response對象中添加上下文,這樣就能實現內容通訊。瀏覽器
另外裝飾器能夠在接收到輸入錯誤的request.data時拋出ParseError異常,或者在適當的時候返回405 Method Not Allowed狀態碼。app
Okay, let's go ahead and start using these new components to write a few views.框架
We don't need our JSONResponse
class in views.py
any more, so go ahead and delete that. Once that's done we can start refactoring(重構) our views slightly.
在views.py文件中咱們再也不須要咱們的JSONResponse類
,因此繼續刪除。一旦完成,咱們能夠開始細微地重構咱們的視圖。
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)
能夠看出,通過改進的代碼已經把上面所說的幾個django-rest-framework帶來的特性都應用起來了,咱們能夠看出程序代碼量變少,而且能處理的狀況更多了。 好比說,在本來的視圖函數snippet_detail中,處理'PUT'請求的時候,須要先解析前端發來的json格式的數據再進一步處理:
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
也就是說須要分紅兩步實現,並且這裏有一個限制就是隻能解析json格式的數據流。而改進後的程序只需一行代碼:
serializer = SnippetSerializer(data=request.data)
request.data
can handle incoming json
requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.
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 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)
以上就是對原有的常規的Django視圖函數的改進。
總結一下就是處理request提交過來的數據不須要必定是json格式的數據,返回的響應也不須要必定是json數據,也能夠是通過渲染的HTML頁面。稍後就會示範使用。
既然上面已經說了返回給客戶端的Response但是json或者是HTML等格式的內容,那麼用戶在使用的時候是如何指定返回哪一種格式的內容呢,那就是在URL的最後加上後綴。好比http://127.0.0.1:8000/snippets.json,這樣就是用戶本身指定了返回json格式的Response,而不是咱們在後臺指定返回固定的格式。
只需對咱們的程序稍加改進就能夠了,在兩個視圖函數添加關鍵詞參數format:
def snippet_list(request, format=None):
and
def snippet_detail(request, pk, format=None):
Now update the urls.py
file slightly, to append a set of format_suffix_patterns(格式後綴模式)
in addition to the existing URLs.
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)
Go ahead and test the API from the command line, as we did in tutorial part 1. Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests.
We can get a list of all of the snippets, as before.
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" } ]
We can control the format of the response that we get back, either by using the Accept
header:
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
Or by appending a format suffix:
http http://127.0.0.1:8000/snippets.json # JSON suffix
http http://127.0.0.1:8000/snippets.api # Browsable API suffix
Similarly, we can control the format of the request that we send, using the Content-Type
header.
# 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" }
If you add a --debug
switch to the http
requests above, you will be able to see the request type in request headers.
Now go and open the API in a web browser, by visiting http://127.0.0.1:8000/snippets/.
Because the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation.
Having a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API.
See the browsable api topic for more information about the browsable API feature and how to customize it.
因爲API根據客戶端請求選擇響應的內容類型,所以默認狀況下,當Web瀏覽器請求資源時,將返回HTML格式的資源表示形式。這容許API返回徹底的可瀏覽網頁的HTML表示。
擁有一個可瀏覽網頁的API是一個巨大的可用性勝利,而且使開發和使用您的API更容易。它也大大下降了想要檢查和使用您的API的其餘開發人員的入門障礙。