教程 2:Requests 和 Responses
从本章开始,我们将开始探索框架的核心,这里先介绍一些重要的概念。
# Request 对象
REST framework 使用一个叫Requests
的对象扩展了原生的HttpRequest
,并提供了更灵活的请求处理。Requests
对象的核心属性就是request.data
,和requests.POST
类似,但更强大:
request.POST # 只处理form数据.只接受'POST'方法.
request.data # 处理任意数据.接受'POST','PUT'和'PATCH'方法.
2
# Response 对象
REST framework 也提供了一个获取未渲染(unrendered)内容为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
、并处理在使用格式错误的输入来访问request.data
时发生的任何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)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
改造后的代码更简洁了,并且更像 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)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
一切都是如此熟悉,这和原生的 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):
pass
2
和
def snippet_detail(request, pk, format=None):
pass
2
然后修改urls.py
,添加format_suffix_patterns
:
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets/<int:pk>', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
2
3
4
5
6
7
8
9
10
我们不需要添加任何额外信息,它给我们一个简单清晰的方法去获取指定格式的数据。
# 测试
和上一章一样,我们使用命令行来进行测试。尽管为了更好地处理异常添加了相应的错误处理,但一切还是那么简单:
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"
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
我们可以通过控制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
2
或者通过添加格式后缀:
http http://127.0.0.1:8000/snippets.json # JSON suffix
http http://127.0.0.1:8000/snippets.api # Browsable API suffix
2
同样的,我们可以通过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"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
或打开浏览器访问http://127.0.0.1:8000/snippets/
# 易读性
由于 API 根据客户端发出的请求来决定返回的数据类型,当我们使用浏览器访问时默认要求返回 HTML 数据格式,这使我们的 API 在浏览器访问时结果格式十分易读。
使用浏览器访问时返回易于阅读结果是一个巨大的进步,这让我们更容易的开发、使用 API。这使其他人员更加方便的使用、检查 API。
# 接下来
在第 3 部分中,我们将开始使用基于类的视图,并看看泛型视图如何减少我们需要编写的代码量。