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
]
Django settings.py

 

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
]
Django urls.py

 

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
Django models.py

 

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
]
Django rest_urls.py

 

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")
Django rest_serializer.py

 

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
Django rest_views.py

 

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 views.py

 

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

 

posted @ 2017-07-10 14:55  Vincen_shen  阅读(316)  评论(0)    收藏  举报