教程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也提供了一个类型为TemplateResponseResponse对象,它返回类型由数据决定。(原文如下,这句话我怎么翻译都觉得别扭,意思就是客户端要神码类型它返回神码类型)

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种装饰器来编写视图:

  1. 基于函数视图的@api_view
  2. 基于类视图的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。

results matching ""

    No results matching ""