教程2:请求和响应
从这一点来说,我们将真正开始覆盖REST框架的核心。我们来介绍一些基本的构建块。
请求对象
REST框架引入了一个Request
扩展常规的对象HttpRequest
,并提供更灵活的请求解析。Request
对象的核心功能是与Web API request.data
类似的属性request.POST
,但更为有用。
request.POST # Only handles form data. Only works for 'POST' method.
request.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.
响应对象
REST框架还引入了一个Response
对象,它是一种类型的对象,它TemplateResponse
使用未呈现的内容并使用内容协商来确定返回给客户端的正确内容类型。
return Response(data) # Renders to content type as requested by the client.
状态码
在视图中使用数字HTTP状态代码并不总是显而易见的阅读,并且如果错误代码错误,很容易不注意。REST框架为每个状态代码提供更明确的标识符,例如HTTP_400_BAD_REQUEST
在status
模块中。这是一个好主意,而不是使用数字标识符。
包装API视图
REST框架提供了两个可用于编写API视图的包装器。
-
@api_view
用于处理基于函数的视图的装饰器。 - 该
APIView
班与基于类的视图工作。
这些包装提供了一些功能,例如确保Request
在视图中接收实例,并向Response
对象添加上下文,以便可以执行内容协商。
这些包装器还提供了一些行为,例如405 Method Not Allowed
在适当时返回响应,以及处理ParseError
在request.data
格式错误的输入中访问时发生的任何异常。
把它们结合在一起
好的,让我们继续,开始使用这些新的组件来写几个意见。
我们不再需要我们的JSONResponse
类,在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):
"""
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)
我们的实例视图是对前一个示例的改进。它更简洁一些,现在的代码感觉和我们使用Forms API非常相似。我们还使用了指定的状态码,这使得响应的含义更加明显。
以下是views.py
模块中单个片段的视图。
@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.data
可以处理传入的json
请求,但也可以处理其他格式。同样,我们使用数据返回响应对象,但允许REST框架将响应呈现给我们正确的内容类型。
添加可选的格式后缀到我们的网址
为了利用我们的响应不再被硬连接到单个内容类型的事实,让我们添加对格式后缀的支持到我们的API端点。使用格式后缀给了我们明确引用给定格式的URL,并且意味着我们的API将能够处理诸如http://example.com/api/items/4.json之类的 URL 。
通过向format
两个视图添加关键字参数开始,就像这样。
def snippet_list(request, format=None):
和
def snippet_detail(request, pk, format=None):
现在snippets/urls.py
稍微更新文件,以附加一组format_suffix_patterns
现有的URL。
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[0-9]+)$', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
我们不一定需要添加这些额外的url模式,但它给了我们一个简单,干净的方式来引用一个特定的格式。
它看起来如何?
继续从命令行测试API,就像我们在教程第一部分中所做的那样。一切工作都非常类似,尽管如果我们发送无效的请求,我们已经有了更好的错误处理。
和以前一样,我们可以得到所有片段的列表。
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"
}
如果您将--debug
开关添加到上述http
请求中,您将能够在请求标头中看到请求类型。
(blog) [root@centos7-01 django_auth_example]# http --form --debug POST http://192.168.152.131:5000/snippets/ code="import sys"
HTTPie 0.9.9
Requests 2.18.4
Pygments 2.2.0
Python 3.6.2 (default, Sep 12 2017, 18:57:38)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]
/root/.pyenv/versions/3.6.2/envs/blog/bin/python
Linux 3.10.0-693.2.2.el7.x86_64
",
"stderr_isatty": true,
"stdin": "<_io.TextIOWrapper name='' mode='r' encoding='UTF-8'>",
"stdin_encoding": "UTF-8",
"stdin_isatty": true,
"stdout": "<_io.TextIOWrapper name='' mode='w' encoding='UTF-8'>",
"stdout_encoding": "UTF-8",
"stdout_isatty": true
}>
>>> requests.request(**{
"allow_redirects": false,
"auth": "None",
"cert": "None",
"data": {
"code": "import sys"
},
"files": {},
"headers": {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"User-Agent": "HTTPie/0.9.9"
},
"method": "post",
"params": {},
"proxies": {},
"stream": true,
"timeout": 30,
"url": "http://192.168.152.131:5000/snippets/",
"verify": true
})
HTTP/1.0 201 Created
Allow: OPTIONS, POST, GET
Content-Type: application/json
Date: Tue, 30 Jan 2018 09:14:17 GMT
Server: WSGIServer/0.2 CPython/3.6.2
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
{
"code": "import sys",
"id": 5,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
}
现在,通过访问http://127.0.0.1:8000/snippets/,在Web浏览器中打开API 。
浏览功能
由于API根据客户端请求选择响应的内容类型,因此默认情况下,当Web浏览器请求资源时,将返回HTML格式的资源表示形式。这允许API返回完全的可浏览网页的HTML表示。
拥有一个可浏览网页的API是一个巨大的可用性的胜利,使开发和使用您的API更容易。它也大大降低了想要检查和使用您的API的其他开发人员的进入门槛。
有关可浏览的API功能以及如何对其进行定制的更多信息,请参阅可浏览的API主题。
下一步是什么?
在教程第3部分中,我们将开始使用基于类的视图,并查看通用视图如何减少我们需要编写的代码量。