何为REST

即”Representational State Transfer”,翻译为”表现层状态转化”,该概念来源于作者Roy Thomas Fielding在其博士论文中的内容,我们根据其中的几句话大概理解下:

REST is a hybrid style derived from several of the network-based architectural styles and combined with additional constraints that define a uniform connector interface.

大概意为REST是一种的软件(这里即WEB)架构风格,它提供了一个唯一的连接接口。

REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems.

大概意为REST这种架构风格提供了一些限制,包括可控制的交互、一般的接口、独立的开发环境、中间件、安全性、封装性等功能。

所有以上说的目的就是要实现一种前后端分离的架构模式,后端只负责提业务需要用到的数据API,前端只负责调用后端提供的API数据作展示,这些行为都要遵守REST风格。

设计RESTful API

本人借鉴了阮一峰的文章,摘抄了以下几点,都为暂时遇到的,以后可能会更新。

  1. 路径(Endpoint): 路径又称”终点”,表示API的具体网址。在RESTful架构中,每个网址代表一种资源。
  2. HTTP动词:对于资源的具体操作类型,由HTTP动词表示。常用的HTTP动词有下面五个。
    • GET:从服务器取出资源。
    • POST:在服务器新建一个资源。
    • PUT:在服务器更新资源(客户端提供改变后的完整资源)。
    • PATCH:在服务器更新资源(客户端提供改变的属性)。
    • DELETE:从服务器删除资源。
  3. 状态码(Status Codes):服务器向用户返回的状态码和提示信息,常见的有以下一些。
    • 200 OK - 服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    • 201 CREATED - 用户新建或修改数据成功。
    • 403 Forbidden - 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    • 404 NOT FOUND - 用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    • 415 UNSUPPORTED_MEDIA_TYPE - 请求的格式不受请求页面的支持。
    • 500 INTERNAL SERVER ERROR - 服务器发生错误,用户将无法判断发出的请求是否成功。

理解以上几点我们便可以写一些普通的RESTful API了。

Django REST framework 使用

Serializers

所谓serializing目的就是要将后端返回给前端的querysets 或者 model instances(模型实例)转化Python的数据类型然后渲染为JSON、XML等数据格式返回给前端,而deserializing目的就是将前端提交的数据转化为后端的格式进行操作。有多种声明serializer的方式我这里介绍2种。

我们的Model模型:

1
2
3
4
5
6
from django.contrib.auth.models import User
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
birthday = models.DateField(u'生日', blank=True, null=True)

第一种:

1
2
3
4
5
6
7
8
9
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
email = serializers.ReadOnlyField()
profile = UserProfileSerializer()
class Meta:
model = User
fields = ('id', 'email', 'username', 'first_name', 'last_name', 'profile')

serializer与Django的forms有很大地方是很类似的但是有几个地方是值得说明的:

  1. serializer会默认提供一个字段,根据继承的父类,若是Serializer则提供id,若继承HyperlinkedSerializer则会提供类似于url形式的主键字段。
  2. serializer提供了多重字段,比如ReadOnlyField表示只读。
  3. erializer还提供了表示模型之间关系的字段如PrimaryKeyRelatedField,我们这里不作详细说明,以后遇到补充。

第二种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib.auth.models import User
from .models import UserProfile
from rest_framework import serializers
class UserSerializer(serializers.Serializer):
email = serializers.ReadOnlyField()
username = serializers.CharField()
first_name = serializers.CharField()
last_name = serializers.CharField()
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('id', 'birthday', 'code_language', 'challenge_default')

采用继承ModelSerializer的方式比上面的方式更加简洁,它会自动的将生命的model字段转化为serializer字段,值得注意的是我们profile这个字段,其以嵌套的方式声明了调用UserProfileSerializer,这样就可以在update的时候在一个serializer中序列两个模型的字段。

Permissions

所谓的Permissions其作用就是对请求request做出限制,它有两个方法has_permission和has_obj_permission,需要先满足has_permission才能在执行has_obj_permission,我们一般就用一个就行了。

1
2
3
4
5
6
7
8
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.owner == request.user

这个限制了,若请求类型属于SAFE_METHODS元组中,则可以访问,若不在,只有是对象的拥有者才可以进行PUT、POST等非SAFE_METHODS中类型的请求。

Class-based Views

基于类的视图,每个类继承类一些基础类,获得继承的基础类的包含的方法,可继承的基础类有APIView、mixins.(代表各种操作的类)、viewsets.(包括各种viewset,每种viewset都包含特定的方法)等,我们这里介绍第二种。

1
2
3
4
5
6
7
8
9
10
11
12
from django.contrib.auth.models import User
from rest_framework import mixins
from rest_framework import viewsets
from accounts.permissions import UserProfilePermission
from accounts.serializers import UserSerializer
class UserProfileViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (UserProfilePermission,)

类中的三个属性是缺一不可,分别代表返回特定model的queryset、要使用的serializer以及使用该API需要的权限。继承的GenericViewSet只包含get_object, get_queryset两个方法,不包含其他方法,使用该类可自由的继承实现特定的方法。mixins.RetrieveModelMixin类包括了GET操作的方法,UpdateModelMixin类包括了PUT操作方法所以这里我们只实现了以上两种操作,对于访问该API的其他方法,我们都会返回404或405状态码。

Routers

由于我们使用了第二种方式定义了视图,所以我们使用Routers定义url资源,其他方式我们暂且不表。

1
2
3
4
5
6
7
8
9
10
11
12
13
from django.conf.urls import patterns, include, url
from rest_framework import routers
from accounts.api import UserProfileViewSet
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'profile', UserProfileViewSet)
urlpatterns = patterns(
'api',
url(r'^', include(router.urls)),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
)

总结

到这里我们就完成了一个API的设计,总体来说使用框架来实现API还是很方便的,通过对这个框架的使用我觉的自己还有一些知识需要巩固,包括HTTP协议、Python的面向对象设计等。只会使用框架还是很单薄的,对自己的成长还是帮助甚微的。