RESTful Web Services——3:使用API类视图实现通用化功能

导读:本篇文章讲解 RESTful Web Services——3:使用API类视图实现通用化功能,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1.利用模型序列化器的优势

在之前的篇幅中我们已经创建了Toy模型以及它的序列化器ToySerializer,并使用一些工具来验证序列化与反序列化功能。在ToySerializer序列化器中我们定义了许多与模型属性同名的属性并重写了createupdate方法。

然而我们重复了许多已被包含在模型当中的代码和信息,比如字段类型、字段参数等。

现在,我们打算使用model serializers来精简代码并避免信息重复,这由定义一个新的序列化器来实现,它将继承 rest_framework.serializers.ModelSerializer而不是像之前那样继承rest_framework.serializers.Serializer

ModelSerializer将会根据我们指定的模型来填充默认字段及validators,这种思路与django中的ModelForm类似。此外,它还提供默认的createupdate接口,我们能利用它们来实现适当的createupdate方法。

在应用的serializers.py文件中创建序列化器:

from rest_framework import serializers
from toys.models import Toy


class ToySerializer(serializers.ModelSerializer):
    class Meta:
        model = Toy
        fields = ('id',
                  'name',
                  'description',
                  'release_date',
                  'toy_category',
                  'was_included_in_home')

这个新的序列化器ToySerializer继承了serializers.ModelSerializer,只定义了一个Meta内部类,该内部类中有两部分:

  • model:关联的模型
  • fields:所关联模型中需要序列化的字段

这里没有重写createupdate方法,是因为ModelSerializer这个超类已经提供了这些方法的接口,直接使用就行。

好了,第一个待优化的点完成了,接下来就可以使用RESTful Web Services: Creating API Views中提到的工具发送各种HTTP请求了,所达到的效果是一样的。

2.理解被接受的与被返回的内容类型

RESTful Web Services: Creating API Views的toys应用的views.py文件中我们已经创建了两个函数视图,这些函数会在必须返回JSON数据时返回JSONResponse以及一个HTTP状态码。无论它们收到什么类型的HTTP请求,都会在响应体中返回的JSON数据类型。

使用postman发送一个指定请求头中Acceptapplication/json的GET请求:
在这里插入图片描述
将得到如下响应:
在这里插入图片描述

在这里插入图片描述

还是使用postman发送一个指定请求头中Accepttext/html的GET请求,也将得到Content-Typeapplication/json的响应。尽管前者application/json指定接收JSON格式的响应,后者text/html指定接收text/html格式的响应。

虽然可以通过编写大量的代码将内容转化为响应中Content-Type的格式,但使用REST framework提供的API才是好主意。

3.使用命令行工具创建不被支持的HTTP OPTIONS请求

有时我们可能不知道一个资源所支持的HTTP方法是什么,这可以通过编写并发送带有OPTIONS动作的HTTP请求来避免问题。

OPTIONS请求旨在发送一种“探测”请求以确定针对某个目标地址的请求必须具有怎样的约束(比如应该采用怎样的HTTP方法以及自定义的请求报头),然后根据其约束发送真正的请求。比如针对“跨域资源”的预检(Preflight)请求采用的HTTP方法就是OPTIONS。

如果RESTful Web服务为资源或资源集合实现OPTIONS方法,它将会在请求响应头中创建Allow 键。该键的值将包括它支持的HTTP方法的逗号分隔列表。
此外,响应头将包含有关其他受支持选项的附加信息,例如它可以从请求中解析的内容类型以及它可以在响应中呈现的内容类型。

如果想知道toys资源集合支持哪种HTTP方法,可以通过下面的命令查询:

http OPTIONS :8000/toys/
  • 这会在服务器端产生错误

如果我们使用curl工具执行下面的命令:

C:\Users\PC>curl -iX OPTIONS localhost:8000/toys/
HTTP/1.1 500 Internal Server Error
Date: Sat, 12 Jun 2021 04:48:33 GMT
Server: WSGIServer/0.2 CPython/3.7.6
Content-Type: text/html
X-Frame-Options: SAMEORIGIN
Content-Length: 61505
Vary: Cookie
  • 这也不会获得任何返回HttpResponse响应,因为这里的视图函数只能处理GETPOST请求。这会产生500错误,

这显然都不是我们需要的结果,我们想要的是一致的web服务以及在接收OPTIONS动作后的准确响应。

4.理解装饰器

上面的问题可以通过使用REST framework提供的装饰器来解决。

我们将对上面的两个视图函数使用@api_view装饰器,它允许指定视图函数能处理的HTTP方法。
通过这种方法就能很容易知道某个视图函数支持什么HTTP请求方法。如果请求被分配到某视图函数但该HTTP方法未被装饰器包含,会报405错误。

了解使用@api_view装饰器时发生了什么是很重要的。
它能将基于函数的视图转换为一个rest_framework.views.APIView 类的子类,这个APIView类是所有REST framework视图的基类——这与基于类的视图紧密相关,会在后面用到。

具体来说,我们需要将要支持的HTTP方法加入到装饰器的字符串列表里,它会自动产生对所包含HTTP方法的响应,以及相关的解释器、渲染器。
换句话说,这个响应包含了函数能理解的格式以及函数为这个响应所产生的格式。

到目前为止,函数只能产生JSON格式的响应,但是用了@api_view装饰器,就能确保在视图被调用时从请求中接收不同格式的rest_framework.request.Request类的实例。
此外,该装饰器还能处理我们访问request.data属性时某些所需的 ParserError以及解析错误。

5.使用装饰器激活不同的解析器与渲染器

我们将进行必要的更改,以使用先前引入的@api_view装饰器,以利用通过APIView类提供的通用行为,使其与不同的解析器和渲染器一起工作。

接下来需要重新编写views.py中的内容:

from django.shortcuts import render
from rest_framework import status
from toys.models import Toy
from toys.serializers import ToySerializer
from rest_framework.decorators import api_view
from rest_framework.response import Response


@api_view(['GET', 'POST'])	#能处理GET与POST方法
def toy_list(request):
    if request.method == 'GET':
        toys = Toy.objects.all()
        toys_serializer = ToySerializer(toys, many=True)
        return Response(toys_serializer.data)
    elif request.method == 'POST':
        toy_serializer = ToySerializer(data=request.data)
        if toy_serializer.is_valid():
            toy_serializer.save()
            return Response(toy_serializer.data,
                            status=status.HTTP_201_CREATED)
        return Response(toy_serializer.errors,
                        status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PUT', 'DELETE'])	#能处理GET、POST与PUT方法
def toy_detail(request, pk):
    try:
        toy = Toy.objects.get(pk=pk)
    except Toy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    elseif request.method == 'GET':
	        toy_serializer = ToySerializer(toy)
	        return Response(toy_serializer.data)
	    elif request.method == 'PUT':
	        toy_serializer = ToySerializer(toy, data=request.data)
	        if toy_serializer.is_valid():
	            toy_serializer.save()
	            return Response(toy_serializer.data)
	        return Response(toy_serializer.errors,
	                        status=status.HTTP_400_BAD_REQUEST)
	    elif request.method == 'DELETE':
	        toy.delete()
	        return Response(status=status.HTTP_204_NO_CONTENT)    
  • 看见没有,@api_view装饰器的使用还是很简单的。

代码移除了之前的定义的JSONResponse类,使用更加通用的rest_framework.response.Response进行序列化响应。
代码还移除了对rest_framework.parsers.JSONParser类的使用,直接使用toy_serializer = ToySerializer(data=request.data)替代了下面两句进行反序列化解析:

toy_data = JSONParser().parse(request)
toy_serializer = ToySerializer(data=toy_data)

这就优化了第二个点,让代码更加精简。

6.利用内容协商类

APIView类为每个视图定义了一些默认设置,可以在 settings.py 中指定所需的值来覆盖,也可以在子类中覆盖类属性。
通常情况下,不会更改默认设置,但是必须了解APIView类使用的默认设置,因为我们添加的@api_view装饰器,它自动使用这些设置,特别是在进行内容协商的时候。

1,解析器

DEFAULT_PARSER_CLASSES项的值指定一个字符串元组,指示我们要用于解析后端的全局默认的解析器类集:

(
	'rest_framework.parsers.JSONParser',
	'rest_framework.parsers.FormParser',
	'rest_framework.parsers.MultiPartParser'
)

当使用@api_view装饰器时,RESTful Web 服务能选择合适的解析器来处理不同的内容类型——当访问request.data时,REST框架将检查传入请求中的Content-Type头以确定用于解析请求内容的解析器:
在这里插入图片描述
因为当在函数中访问 request.data属性时,REST framework会检查请求头中Content-Type的值并决定合适的解析器来解析请求内容,所以说开发客户端应用程序时应该必须指定请求头中的Content-Type

2,渲染器

DEFAULT_RENDERER_CLASSES项的值指定一个字符串元组,指示我们要用于渲染后端的全局默认的渲染器类集:

(
	'rest_framework.renderers.JSONRenderer',
	'rest_framework.renderers.BrowsableAPIRenderer',
)

当使用@api_view装饰器时,RESTful Web 服务能选择合适的渲染器来渲染不同的内容类型。像上面一样,我们可以进行必要的更改以使用 rest_framework.response.Response 实例来使用这些内容类型:
在这里插入图片描述
到目前为止,我们了解解析器和渲染器的默认设置。 接下来就必须根据请求中指定的要求为响应选择适当的渲染器。

3,内容协商

内容协商就是根据客户端或服务器的通信中HEAD的一些选项,从多个可能的表示方法中选择一个合适的类型协商内容格式的过程。

这个过程由DEFAULT_CONTENT_NEGOTIATION_CLASS项指定,它有一个默认值:
rest_framework.negotiation.DefaultContentNegotiation类,该类会基于接收到的请求信息为响应选择合适的渲染器。
例如,如果请求指定Accepttext/html,则该内容协商类会选择rest_framework.renderers.BrowsableAPIRenderer去渲染响应并生成text/html媒体类型而不是 application/json

在老版本代码中我们使用了JSONRenderer、JSONResponse、HttpResponse,而在新版本中使用 rest_framework.response.Response,通过内容协商特性,Response类会将被提供的数据渲染为合适的内容类型并将结果返回给提交请求的客户端,非常简洁。

7.使用命令行工具编支持 HTTP OPTIONS 方法的请求

如果我们想知道toys集合支持哪些HTTP方法,也就是说我们想使用OPTIONS方法,那就继续使用我们前面用到的命令,这里就不会再报错了:

http OPTIONS :8000/toys/

由于使用了@api_view装饰器,它使函数能够确定所支持的HTTP动作、所启动的解析与响应选项。结果如下:

C:\Users\PC>http OPTIONS :8000/toys/
HTTP/1.1 200 OK
Allow: POST, OPTIONS, GET
Content-Length: 167
Content-Type: application/json
Date: Mon, 14 Jun 2021 05:43:50 GMT
Server: WSGIServer/0.2 CPython/3.7.6
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "description": "",
    "name": "Toy List",
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "renders": [
        "application/json",
        "text/html"
    ]
}
  • 如所见的那样,Allow的值是一个由请求资源集合所支持且使用逗号分割的HTTP动作表。
  • 由于请求没有指定内容类型,所以视图函数使用默认的application/json来渲染响应的内容类型。

使用CURL工具执行:

curl -iX OPTIONS localhost:8000/toys/2

结果如下:

HTTP/1.1 200 OK
Date: Mon, 14 Jun 2021 05:49:42 GMT
Server: WSGIServer/0.2 CPython/3.7.6
Content-Type: application/json
Vary: Accept, Cookie
Allow: OPTIONS, DELETE, PUT, GET
X-Frame-Options: SAMEORIGIN
Content-Length: 169

{"name":"Toy Detail","description":"","renders":["application/json","text/html"],"parses":["application/json","application/x-www-form-urlencoded","multipart/form-data"]}

资源和资源集合可以解析和呈现相同的内容类型,因为所有内容都由装饰器和类处理。

8.使用不同的内容类型

在之前,我们在一些请求中使用了-H “Content-Type: application/json”来指定CURL工具发送application/json格式的数据,之所以要这样,是因为CURL中默认的内容类型是application/x-www-form-urlencoded

当然,现在的版本已不再局限于使用JSON格式,它还支持在POST与PUT中使用application/x-www-form-urlencodedmultipart/form-data

接下来我们要在HTTPPie工具中使用 -f 选项来编写一条请求:

  • -f 将命令中的数据项序列化为表单字段吗,并将请求头中的 Content-Type 设置为 application/x-www-form-urlencoded

在这里插入图片描述
下面是等效的 curl 命令:
在这里插入图片描述
上面两条命令的作用就是创建一个新的toy实例,这是因为rest_framework.parsers.FormParser类能够解析请求中的的数据,如果数据有效,还会在创建实例后进行保存。响应内容如下:
在这里插入图片描述

9.使用不被支持的HTTP方法发送HTTP请求

现在来发送一个资源集合不支持的HTTP请求:

curl -iX PATCH localhost:8000/toys/

结果就是该方法 not allowed:

HTTP/1.1 405 Method Not Allowed
Date: Thu, 22 Oct 2020 00:27:24 GMT
Server: WSGIServer/0.2 CPython/3.7.6
Content-Type: application/json
Vary: Accept, Cookie
Allow: POST, OPTIONS, GET
X-Frame-Options: SAMEORIGIN
Content-Length: 42

{"detail":"Method \"PATCH\" not allowed."}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/98120.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!