RESTful API设计指南
RESTful API是什么?
RESTful是 Representational State Transfer的缩写(翻译为:表现层状态转移)。这个中文直译经常出现在很多博客中。尼玛谁听得懂“表现层状态转移”?这是人话吗?首先,之所以晦涩是因为前面主语被去掉了,全称是 Resource Representational State Transfer:通俗来讲就是:资源在网络中以某种表现形式进行状态转移。分解开来:
Resource: 资源,即数据,比如Newsfeed, friends等等;
Representational: 某种表现形式,比如Json, XML, JPEG等等;
State Transfer: 状态变化, 通过HTTP动词实现(如:get, post, put, delete)。
REST拥有一组架构约束条件和原则,只要符合这一套约束原则的架构,就是RESTful架构。需要注意的是,REST并没有提供新的组件、技术,也不是专门为HTTP提供规范,而是通过约束和原则去合理使用WEB的现有特征和能力。RESTful API是一种围绕 资源(Resource) 展开的 无状态传输 的API设计方案。
RESTful API在功能上更像是隔离层, 要访问服务器资源,就必须找到API入口。如果这个入口的规则遵循REST风格,那就是RESTful设计框架。
资源的表示URI
REST的名称“表现层状态转化”中,省略了主语: “表现层”其实指的是“资源”(Resource)的“表现层”。
资源是一个数据单元,这个单元可大可小,根据业务规模自主定制。要准确识别一个资源,需要有一个唯一的标识,在WEB中这个唯一的标识就是URI(Uniform Resource Identifier)
URI的设计应该具有自释性,可寻址性,直观性的原则。用/来表示顶层,用_或- 来分割单词,用?来过滤资源。
URI有两种表现形式: URL和URN,URN仍然处于测试阶段,因此现在所的URI就是指URL
HTTP动词
GET: 从服务器取出资源(一项或多项)
POST: 在服务器创建一个资源
PUT: 在服务器更新资源(客户端提供更改后的完整资源)
PATCH: 在服务器更新资源(客户端提供改变的属性)(不常用)
DELETE: 从服务器删除资源
HEAD:获取资源的元数据(不常用)
OPTIONS: 获取信息,关于资源的哪些属性是客户端可以改变的(不常用)
示例:
GET /zoos:列出所有动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的所有动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 BAD REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
Server的API如何设计才满足Restful要求?
1、URL root:
example.org/api/v1/*
api.example.com/v1/*
2、API Versioning:
/api/v1/
3、URI使用名词,而不是动词,且推荐用复数
Bad
/getProducts /listOrders /retrieveClientByOrder?orderId=1
Good
GET /products : will return the list of all products POST /products : will add a product to the collection GET /products/4 : will retrieve product #4 PUT /products/4 : will update product #4
Django Rest Framework
Django REST framework is a powerful and flexible toolkit for building Web APIs.
配置示例:
通过 rest framework实现user表和 course表 API

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'SCMC', 'rest_framework' # 需要先安装Django rest framework ]

urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.user_login, name="login"), url(r'^logout/$', views.user_logout, name="logout"), url(r'^index/$', views.index, name="index"), url(r'^api/', include("SCMC.rest_urls")) # api 需要的url ]

class Course(models.Model): """课程详情""" name = models.CharField(max_length=32, unique=True) period = models.IntegerField(verbose_name="课程周期(月)") price = models.IntegerField(verbose_name="课程价格") outline = models.TextField(verbose_name="课程大纲") def __str__(self): return self.name class MyUser(AbstractBaseUser): username = models.CharField(max_length=32, unique=True, verbose_name="username") email = models.EmailField(max_length=128, unique=True, verbose_name="email address") is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) role = models.ForeignKey("Role", null=True, blank=True) customer = models.OneToOneField("Customer", null=True, blank=True) objects = MyUserManager() USERNAME_FIELD = 'username' REQUIRED_FIELDS = ["email"] def get_full_name(self): return self.username def get_short_name(self): return self.username def __str__(self): return self.username def has_perm(self, perm, obj=None): """Does the user have a specific permission?""" # Simplest possible answer: Yes, always return True def has_perms(self, perms, obj=None): """Does the user have a specific permission?""" # Simplest possible answer: Yes, always return True def has_module_perms(self, app_label): """Does the user have permissions to view the app `app_label`?""" # Simplest possible answer: Yes, always return True @property def is_staff(self): return self.is_admin

from django.conf.urls import url, include from rest_framework import routers from .rest_views import UserViewSet from .views import courses_api, course_detail router = routers.DefaultRouter() router.register(r'users', UserViewSet) urlpatterns = [ url(r'^', include(router.urls)), # rest framework 自带的API url url(r'^api-auth/', include("rest_framework.urls", namespace="rest_framework")), url(r'^courses/$', courses_api), # 自定义的API url url(r'^courses/(\d+)$', course_detail) # 自定义的API url ]

from rest_framework import serializers from .models import MyUser, Course class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = MyUser fields = ("url", "username", "email", "is_active", "is_staff") class CourseSerializer(serializers.ModelSerializer): class Meta: model = Course fields = ("id", "name", "period", "price", "outline")

from rest_framework import viewsets from .models import MyUser from .rest_searilizers import UserSerializer class UserViewSet(viewsets.ModelViewSet): queryset = MyUser.objects.all() serializer_class = UserSerializer

from .models import Course from django.views import View # Create your views here. from .rest_searilizers import CourseSerializer from rest_framework.response import Response from rest_framework.decorators import api_view from rest_framework import status from django.http import JsonResponse @api_view(["GET", "POST"]) def courses_api(request): """Get all course list, or create a new course""" if request.method == "GET": courses = Course.objects.all() serializer = CourseSerializer(courses, many=True) return Response(serializer.data, status=status.HTTP_200_OK) if request.method == "POST": print(request) serializer = CourseSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(status=status.HTTP_400_BAD_REQUEST) @api_view(["GET", "PUT", "DELETE"]) def course_detail(request, course_id): """Retrieve, update or delete a course""" course_obj = Course.objects.filter(id=course_id).first() if not course_obj: return Response(status=status.HTTP_400_BAD_REQUEST) if request.method == "GET": serializer = CourseSerializer(course_obj) return Response(serializer.data, status=status.HTTP_200_OK) elif request.method == "PUT": serializer = CourseSerializer(course_obj, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.error, status=status.HTTP_400_BAD_REQUEST) elif request.method == "DELETE": course_obj.delete() return Response(status=status.HTTP_204_NO_CONTENT)
Django rest framework Authentication
支持三种认证方式:
BasicAuthentication、SessionAuthentication、TokenAuthentication
默认在全局启用了 BasicAuthentication 和 SessionAuthentication
The default authentication schemes may be set globally, using the 'DEFAULT_AUTHENTICATION_CLASSES' setting. For example.
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) }
Unauthorized and Forbidden responses
- HTTP 401 Unauthorized
- HTTP 403 Permission Denied
Django rest framework Permissions
自带5种认证API, For example:
REST_FRAMEWORK = { # Use Django's standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.DjangoObjectPermissions' # 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', # 'rest_framework.permissions.IsAuthenticated', # 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 'rest_framework.permissions.IsAdminUser' ] }
其他
(1)API的身份认证应该使用OAuth 2.0框架。
(2)服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
参考:
[1]: OAuth2 for Django restAPI
[2]: GitHub API v3
[3]: Django rest framework
[4]: http://blog.csdn.net/huanhu821011/article/details/41560503
[5]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html#model-serializers