天天看点

Django RESTful API (2) 请求和响应

欢迎关注个人微信公众号,大大大碗面,不定期分享AI论文解读和开发技术,互联网小白,轻喷~

Django RESTful API (2) 请求和响应

Request对象

平时我们在写Django的视图函数的时候,都会带上一个request参数,这样就能处理平时搭建网站时,浏览器访问网页时发出的常规的HttpRequest。但是现在我们导入了django-rest-framework,它能够对request进行拓展,并且提供更灵活的请求解析。这个特性体现在哪呢?请看下面这个例子:

request.POST  # 只能处理表单数据.只能处理POST请求
request.data  # 能处理各种数据。  可以处理'POST', 'PUT' 和 'PATCH'模式的请求
           

Response对象

和request对象一样,django-rest-framework也对其进行了很实用的拓展,在我上一篇文章的snippets/views.py中,我们导入了JsonResponse用于返回json格式的响应,在视图函数中是这样的:

@csrf_exempt
def snippet_list(request):
    '''
    列出所有已经存在的snippet或者创建一个新的snippet
    '''
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)

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

也就是说,在return的时候就需要指明json格式,这样显得很不实用而且很单一,所以经过拓展后的Reponse对象就很方便了,它会根据客户端的请求头部信息来确定正确的内容类型以返回给客户端。只需如下代码:

状态码

我们知道发送http请求时会返回各种各样的状态吗,但是都是简单的数字,比如200、404等,这些纯数字标识符有时候可能不够明确或者客户端在使用的时候不清楚错误信息甚至是没注意看不到,所以django-rest-framework也对此进行了优化,状态码会是HTTP_400_BAD_REQUEST、HTTP_404_NOT_FOUND这种,极大的提高可读性。

装饰API视图

REST框架还提供了一个装饰器和一个类来包装视图函数,可以使用它们来写API视图,让程序能处理的情况更多。

  • @api_view装饰器用在基于视图的++方法++上。
  • APIView类用在基于视图的++类++上。

这两个东西提供的一些功能,让我们省去很多工作,比如说确保你在视图中收到Request对象或在你的Response对象中添加上下文,这样就能实现内容通信。

另外装饰器可以在接收到输入错误的request.data时抛出ParseError异常,或者在适当的时候返回405 Method Not Allowed状态码。

应用

上面说了这么多拓展和优化,接下来就把它们都使用起来,改进一下原本的snippets/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):
    """
    列出所有已经存在的snippet或者创建一个新的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)
           

改变一:

在原本的视图函数snippet_detail中,处理’PUT’请求的时候,需要先解析json格式的数据再进一步处理:

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

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

直接使用之前说的request.data就可以获取到提交过来的数据了,并且可以处理各种数据和各种请求动作,方便了开发。

改变二:

return的时候也不需要指定json格式了,由原本的:

改成了:

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

对于另一个视图函数的修改也是同样的原理,这里就不做同样的讲解了,代码直接贴出:

@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)
           

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

向URL添加可选的格式后缀

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

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

以及:

再修改一下snippets/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)   # 导入格式后缀模式
           

改进后的使用

上一节所述的使用方法:

Django RESTful API (2) 请求和响应

也可以通过设置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
           

效果如下:

Django RESTful API (2) 请求和响应

我们可以直接在浏览器输入 http://127.0.0.1:8000/snippets.api 进行查看,会得到一个美观的页面:

Django RESTful API (2) 请求和响应

如果我们需要添加数据,那么我们可以控制 Content-Type 头部信息来提交POST请求:

Django RESTful API (2) 请求和响应

上面说了,改进后可以处理错误的提交,比如把code改成了codes,就会给出错误信息:

Django RESTful API (2) 请求和响应

在请求中如果加入了–debug可以查看到详细的请求信息和类型:

Django RESTful API (2) 请求和响应

在上面介绍@api_view和APIView的时候,提到了在适当的时候返回405 Method Not Allowed状态码。这个所谓适当的时候就要回看到刚才写视图函数的时候,修饰器的代码:

@api_view(['GET','POST'])
@api_view(['GET','PUT','DELETE'])
           

这两行代码就规定了在调用这两个函数,也就是访问到相关的URL时,只能使用指定的请求动作,否则就会报出405 Method Not Allowed错误

正确的使用方式:

http --json PUT http://127.0.0.1:8000/snippets/1.json code="hello wo
rld"
           
Django RESTful API (2) 请求和响应

这样就把id=1的数据修改了。想要删除也是一样的:

Django RESTful API (2) 请求和响应