Django Rest Framework(第二天)—请求与响应

一、请求与响应

从现在开始,我们将真正开始接触REST框架的核心。 我们来介绍几个基本的构建模块。

1.1 请求对象(Request objects)

REST框架引入了一个扩展了常规HttpRequest的Request对象,并提供了更灵活的请求解析。Request对象的核心功能是request.data属性,它与request.POST类似,

request.POST # 只处理表单数据 只适用于'POST'方法

request.data # 处理任意数据 适用于'POST','PUT'和'PATCH'方法

1.2 响应对象(Response objects)

REST框架还引入了一个Response对象,这是一种获取未渲染(unrendered)内容的TemplateResponse类型,并使用内容协商来确定返回给客户端的正确内容类型。

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

1.3 状态码(Status codes)

在你的视图(views)中使用纯数字的HTTP 状态码并不总是那么容易被理解。而且如果错误代码出错,很容易被忽略。REST框架为status模块中的每个状态代码(如HTTP_400_BAD_REQUEST)提供更明确的标识符。使用它们来代替纯数字的HTTP状态码是个很好的主意。

1.4 包装(wrapping)API视图

REST框架提供了两个可用于编写API视图的包装器(wrappers)。

用于基于函数视图的@api_view装饰器。

用于基于类视图的APIView类。

这些包装器提供了一些功能,例如确保你在视图中接收到Request实例,并将上下文添加到Response,以便可以执行内容协商。

包装器还提供了诸如在适当时候返回405 Method Not Allowed响应,并处理在使用格式错误的输入来访问request.data时发生的任何ParseError异常。

1.5 组合在一起

我们在views.py中不再需要JSONResponse类了,所以把它删除掉。删除之后,我们就可以开始重构我们的视图了。

# quickstart/views.py

from .models import Snippet
# from django.views.decorators.csrf import csrf_exempt
from .serializers import SnippetSerializer
from django.http import HttpResponse
# from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

# class JSONResponse(HttpResponse):
#     """
#     继承一个HTTP返回,渲染为JSon返回
#     """
#
#     def __init__(self, data, **kwargs):
#         content = JSONRenderer().render(data)
#         kwargs['content_type'] = 'application/json'
#         super(JSONResponse, self).__init__(content, **kwargs)
#

# @csrf_exempt
@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    列出所有的code snippet,或者创建一个新的Snippet
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        # return JSONResponse(serializer.data)
        return Response(serializer.data)

    elif request.method == 'POST':
        #data = JSONParser().parse(request)
        # serializer = SnippetSerializer(data=data)
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            # return JSONResponse(serializer.data, status=201)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        # return JSONResponse(serializer.errors, status=400)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


# @csrf_exempt
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    """
    获取,更新或者删除一个code snippet
    :param request:
    :param pk:
    :return:
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        # return HttpResponse(status=404)
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        # return JSONResponse(serializer.data)
        return Response(serializer.data)

    elif request.method == 'PUT':
        # data = JSONParser().parse(request)
        # serializer = SnippetSerializer(data=data)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            # return JSONResponse(serializer.data)
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        snippet.delete()
        # return HttpResponse(status=204)
        return Response(status=status.HTTP_204_NO_CONTENT)

可以看出,经过改进的代码已经把上面所说的几个django-rest-framework带来的特性都应用起来了,我们可以看出程序代码量变少,并且能处理的情况更多了。 比如说,在原本的视图函数snippet_detail中,处理'PUT'请求的时候,需要先解析前端发来的json格式的数据再进一步处理:

data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)

也就是说需要分成两步实现,而且这里有一个限制就是只能解析json格式的数据流。而改进后的程序只需一行代码:

serializer = SnippetSerializer(data=request.data)

request.data就可以获取到提交过来的数据了,并且可以处理各种数据和各种请求动作,方便了开发。还有在return的时候也不需要指定json格式了,由原本的:

return JsonResponse(serializer.data, status=201)

改成了

return Response(serializer.data,status=status.HTTP_201_CREATED)

这也意味着返回给客户端的可以是json或者html等格式的内容,返回HTML格式的内容的话,会在浏览器返回经过渲染的、更美观的页面。同时可以看出状态码也改进成了django-rest-framework给我们带来的可读性更高的状态标识码,以上这些措施都很大程度的提高了对客户的友好度。

对于另一个视图函数的修改也是同样的原理,这里就不做同样的讲解了。

以上就是对原有的常规的Django视图函数的改进。

总结一下就是:处理request提交过来的数据不需要一定是json格式的数据,返回的响应也不需要一定是json数据,也可以是经过渲染的HTML页面。稍后就会示范使用。

1.6 给我们的网址添加可选的格式后缀

既然上面已经说了返回给客户端的Response可是json或者是HTML等格式的内容,那么用户在使用的时候是如何指定返回哪种格式的内容呢,那就是在URL的最后加上后缀。诸如http://127.0.0.1:8018/qk/snippets/3.json 之类的URL。这样就是用户自己指定了返回json格式的Response,而不是我们在后台指定返回固定的格式。

只需对我们的程序稍加改进就可以了,在两个视图函数添加关键词参数format:

def snippet_list(request, format=None):


def snippet_detail(request, pk, format=None):

现在更新urls.py文件,给现有的URL后面添加一组 format_suffix_patterns。

from django.conf.urls import url
import quickstart.views as views
from rest_framework.urlpatterns import format_suffix_patterns
app_name = 'qk'

urlpatterns = [
    url(r'qk/snippets/$', views.snippet_list),
    url(r'qk/snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)

二、查看结果

# 安装httpie
pip install httpie

1、我们可以像以前一样获取所有snippet的列表。

~ » http http://127.0.0.1:8018/qk/snippets/                    kenwu@kenwus-MBP
HTTP/1.1 200 OK
Allow: OPTIONS, POST, GET
Content-Length: 320
Content-Type: application/json
Date: Sat, 06 Oct 2018 18:08:43 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

[
    {
        "code": "print \"hello world\"", 
        "id": 3, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": "33344"
    }, 
    {
        "code": "print \"hello world\"\n", 
        "id": 2, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": ""
    }, 
    {
        "code": "foo = \"bar\"\n", 
        "id": 1, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": ""
    }
]

2、我们可以通过使用Accept标头来控制我们回复的响应格式:

# 请求json
~ » http http://127.0.0.1:8018/qk/snippets/ Accept:application/json
HTTP/1.1 200 OK
Allow: OPTIONS, POST, GET
Content-Length: 320
Content-Type: application/json
Date: Sat, 06 Oct 2018 18:12:20 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

[
    {
        "code": "print \"hello world\"", 
        "id": 3, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": "33344"
    }, 
    {
        "code": "print \"hello world\"\n", 
        "id": 2, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": ""
    }, 
    {
        "code": "foo = \"bar\"\n", 
        "id": 1, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": ""
    }
]

# 请求html
~ » http http://127.0.0.1:8018/qk/snippets/ Accept:text/html   kenwu@kenwus-MBP
HTTP/1.1 200 OK
Allow: OPTIONS, POST, GET
Content-Length: 12037
Content-Type: text/html; charset=utf-8
Date: Sat, 06 Oct 2018 18:13:11 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

<!DOCTYPE html>
<html>
  <head>



        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta name="robots" content="NONE,NOARCHIVE" />
        ......

3、通过附加格式后缀:

# json后缀
~ » http http://127.0.0.1:8018/qk/snippets.json                kenwu@kenwus-MBP
HTTP/1.1 200 OK
Allow: OPTIONS, POST, GET
Content-Length: 320
Content-Type: application/json
Date: Sat, 06 Oct 2018 18:14:12 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

[
    {
        "code": "print \"hello world\"", 
        "id": 3, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": "33344"
    }, 
    {
        "code": "print \"hello world\"\n", 
        "id": 2, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": ""
    }, 
    {
        "code": "foo = \"bar\"\n", 
        "id": 1, 
        "language": "python", 
        "linenos": false, 
        "style": "friendly", 
        "title": ""
    }
]


# 浏览器可浏览API后缀
~ » http http://127.0.0.1:8018/qk/snippets.api                 kenwu@kenwus-M
HTTP/1.1 200 OK
Allow: OPTIONS, POST, GET
Content-Length: 12058
Content-Type: text/html; charset=utf-8
Date: Sat, 06 Oct 2018 18:15:00 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

<!DOCTYPE html>
<html>
  <head>



        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta name="robots" content="NONE,NOARCHIVE" />


      <title>Snippet List – Django REST framework</title>
      ......

4、使用Content-Type头控制我们发送的请求的格式。

# POST表单数据
http --form POST http://127.0.0.1:8018/qk/snippets/ code="print 123"

{
  "id": 3,
  "title": "",
  "code": "print 123",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}

# POST JSON数据
http --json POST http://127.0.0.1:8018/qk/snippets/ code="print 456"

{
    "id": 4,
    "title": "",
    "code": "print 456",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

三、浏览功能

由于API根据客户端请求选择响应的内容类型,因此默认情况下,当Web浏览器请求资源时,将返回HTML格式的资源表示形式。这允许API返回完全的可浏览网页的HTML表示。

拥有一个可浏览网页的API是一个巨大的可用性胜利,并且使开发和使用您的API更容易。它也大大降低了想要检查和使用您的API的其他开发人员的入门障碍。


评论(0 ) 点赞(16)


暂未登录,请登录之后发表评论。 QQ