Tutorial 2: 请求与响应

从这开始,我们将接触REST框架的核心。让我们来介绍一系列必要的搭建模块。

请求对象

REST框架介绍了一个请求(Request)对象,它扩展了常规的HttpResquest,并且,提供更灵活的请求解析。请求(Request)对象的核心功能是request.data属性,这个属性与request.POST相似,但是它对Web APIs更加有用。

request.POST # 只处理表单数据。只对'POST'方法起作用。
request.data # 可以处理任意数据。对'POST','PUT'和'PATCH'方法起作用。

响应对象

REST 框架也介绍了Response对象,它是一类用未渲染内容和内容协商来决定正确的内容类型并把它返回给客户端的模板响应(TemplateResponse)

return Response(data) # 根据客户端的请求来渲染成指定的内容类型。

状态码

总是在你的视图中用数字的HTTP状态码会更加容易理解,并且如果你用其他错误代码表示错误,就不太容易注意到了。REST框架为每个状态码(status code)提供更明确的标识符,例如在状态(status)模型中的HTTP_400_BAD_REQUEST。用这些标识符代替纯数字的HTTP状态码是很好的注意。

装饰API视图

REST框架提供两个装饰器,你可以用它们来写API视图。

  • 1 @api_view装饰器用在基于视图的方法上。
  • 2 APIView类用在基于视图的类上。 这些装饰器提供一些功能,例如去报在你的视图中接收Request对象,例如在你的Response对象中添加上下文,这样我们就能实现内容通信。 这里装饰器也提供了一些行为,例如在合适的时候返回405 Method Not Allowed响应,例如处理任何在访问错误输入的request.data时出现的解析错误(ParseError)异常。

结合在一起

好了,让我们开始用这些新的组件写一些视图。 我们不再需要在我们的视图(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 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模块中,有一个独立的snippet视图。

@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.data能够处理json请求,但是它也能处理其他格式。相似地,虽然我们可以在响应对象中带数据,但允许REST框架渲染响应成正确的内容类型。

在我们的链接(URLs)后添加可选格式后缀

为了利用我们的响应内容不再是单一格式的事实,我们应该为我们的API尾部添加格式后缀。用格式后缀给我们明确参考指定格式的URL,这意味着我们的API能够处理像http://example.com/api/items/4/.json一样的链接。 在视图函数中添加一个format参数,像这样:

def snippet_list(request, format=None):

def snippet_list(request, pk, fornat=None):

现在可一很快更新urls.py文件,在已经存在的URL中添加一个格式后缀模式(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)

我们不必添加额外的URL模式,但是它给我们简单、清楚的方式渲染除特定的格式。

看看吧

教程第一部分一样,我们要开始从命令行测试API。虽然我们能在发送无效的请求时更妥当处理错误,但是现在一切都做的够好了。 我们能想之前一样获取所有的snippets列表。

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

或者在URL后添加格式后缀:

http http://127.0.0.1:8000/snippets.json  # JSON 后缀
http http://127.0.0.1:8000/snippets.api   # 浏览用的 API 后缀

同样地,我们可以控制我们发送的请求格式,用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"
}

你也可以从浏览器打开API,通过访问http://127.0.0.1:8000/snippets/

Browsability

因为API是基于客户端请求来选择响应内容的类型,所以默认情况下,在Web浏览器访问资源时,API返回HTML格式的资源。这语序API返回完全可以网页浏览的HTML。 有可以网页浏览API是很好的,这使开发和使用你的API更简单,这也为其他想要查看和使用你的API的开发者大大降低了门槛。 关于可浏览API的特性和如何自定义可浏览API,请见可浏览API话题。

接下来要干什么?

教程的第三部分,我们基于视图用类,并且看看普通的视图我们如何减少代码。