Restful API

作者:王蒙
标签:Django, Web Frameworks, Rest ful, API
简介:不用任何插件,django 就可以实现 restful API。但是在 rest-framework 的帮助下,更容易写出标准,规范的 Restful API。

目标读者

Restful API 相关

预备知识

python, Restful API

问题

  • Restful 常见误区以及 rest-framework 的优势
  • Serializer
  • Parsers/Renderers
  • Generic APIView
  • authentication and permission
  • viewset and router

解决办法

  • Restful 常见误区以及 rest-framework 的优势

    • http 服务,返回 json 就是 restful 接口;返回 html 网页就不是 restful 接口。

      Restful 是为方便前后端沟通而设计的规范。具体特征是: Resource-Oriented, Stateless。 http 协议是网络协议,http 服务可能遵守 Restful API 的规范,也可能不遵守 Restful API 的规范。 http 协议很容易符合 Restful 规范。使用 http 协议注意实现如下几点就能够实现 Restful 规范。

      • Resource-Oriented,尽量用名词设计 API。对于资源的操作,使用 http 方法来完成。
      • 使用 Content-Type 控制资源的表现形式。json,xml 是常用的表现形式,标准的 Restful API 一般会提供多种表现形式,用 Content-Type 确定到底如何展现。
      • GET, HEAD 不修改资源。POST 修改资源。
      • 返回码,返回信息要自说明(self-document)。力求做到用只要看返回,就知道请求的操作怎么样了。
      • 状态转移通过 url 连接跳转。
    • rest-framework 的优势

      刚刚的论述,已经说明不是简单写个返回值是 json 字符串的 view,就能整出标准的 restful 接口(之前这样写的 restful API 是不太规范的)。

      rest-frameworks 为写 Restful API 提供了 GenericView, Parser and Render, Permission Control 。使用这些现成的组件,方便写出规范的 Restful API。

  • Serializer

    Restful API 要做的事儿,可能是去查询数据库。对于数据库定义 Serializer/Deserializer 可以方便的把数据库查询结果整成 Restful 接口的返回值。

    rest-framework 提供了三个 serializer 基类:

    • Serializer: Provides serialization for normal Python class instances
    • ModelSerializer: Provides serialization for model instances
    • HyperlinkedModelSerializer: The same as ModelSerializer, but represents object relationships with links rather than primary keys
    from rest_framework import serializers
    from ..models import Subject
    class SubjectSerializer(serializers.ModelSerializer):
        class Meta:
            model = Subject
            fields = ('id', 'title', 'slug')
    

    对于有外键的 model , serializer 既可以选择呈现外键的值(integer),也可以选择呈现外键对应的对象。

    class CourseSerializer(serializers.ModelSerializer):
        # 选择呈现外键的值(integer)
        modules = ModuleSerializer()
    
        class Meta:
            model = Course
            fields = ('id', 'subject', 'title', 'slug',
                      'overview', 'created', 'owner', 'modules')
    
    class CourseSerializer(serializers.ModelSerializer):
        # 选择呈现外键对应的对象
        modules = ModuleSerializer(many=True)
    
        class Meta:
            model = Course
            fields = ('id', 'subject', 'title', 'slug',
                      'overview', 'created', 'owner', 'modules')
    
  • parsers and renderers

    在 django project 的 settings.py 文件配置 parsers and renders,restful api 就会根据设计根据 Content-Type header 来表现返回结果。上面的 serializer 是把数据转成字典,而 parsers/renders 是把返回结果整成二进制串。

    可能会配置 settings.py 中的 REST_FRAMEWORK -> DEFAULT_RENDERER_CLASSES 项。这样这个 project 默认会用这些 renders。

    parsers 和 renderers 更多细节,参见:

  • Generic API View

    from rest_framework import generics
    from ..models import Subject
    from .serializers import SubjectSerializer
    
    # 如果Restful API 的返回结果是从 QuerySet 中取出来的。那么继承 ListAPIView 和 RetrieveAPIView 可以方便地构造Restful API。
    class SubjectListView(generics.ListAPIView):
        queryset = Subject.objects.all()
        serializer_class = SubjectSerializer
    
    class SubjectDetailView(generics.RetrieveAPIView):
        queryset = Subject.objects.all()
        serializer_class = SubjectSerializer
    
    
    # 如果Restful API 的返回结果不是从 QuerySet 中取出来的。可以继承 APIView 自定义返回值。
    from django.shortcuts import get_object_or_404
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from ..models import Course
    class CourseEnrollView(APIView):
        def post(self, request, pk, format=None):
            course = get_object_or_404(Course, pk=pk)
            course.students.add(request.user)
            return Response({'enrolled': True})
    
  • authentication and permissions

    rest-framework 提供了 BasicAuthentication, TokenAuthenticationSessionAuthentication 类实现认证。

    rest-frameworks 提供了 AllowAny, IsAuthenticated, IsAuthenticatedOrReadOnly, DjangoModelPermissionsDjangoObjectPermissions 类实现权限控制。

    from rest_framework.authentication import BasicAuthentication
    from rest_framework.permissions import IsAuthenticated
    class CourseEnrollView(APIView):
        # 指明采用哪个类,做认证
        authentication_classes = (BasicAuthentication,)
        # 指明是什么什么样的权限控制,比如这里 IsAuthenticated 表示只有认证用户才能使用这个 view(API)
        permission_classes = (IsAuthenticated,)
        # ...
    

    继承 rest_framework.permission.BasePermission,重写下面两种方法,可以自定义 permission。

    • has_permission(): View-Level permission check。
    • has_object_permission(): Object-Level permission check。
    from rest_framework.permissions import BasePermission
    class IsEnrolled(BasePermission):
    
    def has_object_permission(self, request, view, obj):
        return obj.students.filter(id=request.user.id).exists()
    
  • viewset and router

    Viewset 可以使用 router 绑定 url。在 Viewset 中 view 比较多时,会很有用。

    from django.conf.urls import url, include
    from rest_framework import routers
    from . import views
    
    
    router = routers.DefaultRouter()
    router.register('courses', views.CourseViewSet)
    
    class CourseViewSet(viewsets.ReadOnlyModelViewSet):
        queryset = Course.objects.all()
        serializer_class = CourseSerializer
    
        @detail_route(methods=['post'],
                      authentication_classes=[BasicAuthentication],
                      permission_classes=[IsAuthenticated])
        def enroll(self, request, *args, **kwargs):
            course = self.get_object()
            course.students.add(request.user)
            return Response({'enrolled': True})
    
        @detail_route(methods=['get'],
                      serializer_class=CourseWithContentsSerializer,
                      authentication_classes=[BasicAuthentication],
                      permission_classes=[IsAuthenticated, IsEnrolled])
        def contents(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    

参考文献