教程2:Requests和Responses
从本章开始,我们将开始探索框架的核心,这里先介绍一些重要的概念。
Request对象
REST framework使用一个叫Requests
的对象扩展了原生的HttpRequest
,并提供了更灵活的请求处理。Requests
对象的核心属性就是request.data
,和requests.POST
类似,但更强大:
request.POST # 只处理form数据.只接受'POST'方法.
request.data # 处理任意数据.接受'POST','PUT'和'PATCH'方法.
Response对象
REST framework也提供了一个类型为TemplateResponse
的Response
对象,它返回类型由数据决定。(原文如下,这句话我怎么翻译都觉得别扭,意思就是客户端要神码类型它返回神码类型)
REST framework also introduces a Response object, which is a type of TemplateResponse that takes unrendered content and uses content negotiation to determine the correct content type to return to the client.
return Response(data) # 返回类型由发出请求的客户端决定
状态码
使用数字HTTP状态码并不总是易于阅读的,并且当你得到一个错误的状态码时不容易引起注意。REST framework为每个状态码都提供了更明显的标志,比如status
模块中的HTTP_400_BAD_REQUEST
,使用它们替代数字是一个好注意。
装饰API视图
REST framework提供了2种装饰器来编写视图:
- 基于函数视图的
@api_view
- 基于类视图的
APIView
这些装饰器提供了少许功能,比如确保在视图中接收Request
实例,添加context到Resonse
对象来决定返回类型。
此外还提供了适当的错误处理,比如405 Method Not Allowed
、有畸形数据传入时引发的ParseError
异常等。
协同工作
现在我们使用这些组件来改写views.
我们不再需要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):
"""
展示或创建snippets.
"""
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。也使用了命名后的状态码让Response含义更加明显。
接下来修改单一的snippets视图:
@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 Form很像。
注意我们并没有显示的声明request和response中的内容类型,request.data
可以处理请求传入的json类型数据,也可以处理其他类型数据。同理,Response对象也可以为我们返回正确的数据类型。
为URL添加可选的数据格式后缀
我们的responses支持多种返回格式,利用这点我们可以通过在URL中添加格式后缀的方法来获取单一数据类型,这意味着我们的URL可以处理类似http://example.com/api/items/4/.json
这样的格式。
首先在views添加format
参数:
def snippet_list(request, format=None):
def snippet_detail(request, pk, format=None):
然后修改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)
我们不需要添加任何额外信息,它给我们一个简单清晰的方法去获取指定格式的数据。
测试
和上一章一样,我们使用命令行来进行测试。尽管添加了相应的错误处理,但一切还是那么简单:
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
控制发送请求的数据类型:
# 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"
}
或打开浏览器访问http://127.0.0.1:8000/snippets/
易读性
由于API根据客户端发出的请求来决定返回的数据类型,当我们使用浏览器访问时默认要求返回HTML数据格式,这使我们的API在浏览器访问时结果格式十分易读。
使用浏览器访问时返回易于阅读结果是一个巨大的进步,这让我们更容易的开发、使用API。这使其他人员更加方便的使用、检查API。