来聊聊这个RESTful
最近这段时间在看面试信息,很多公司的岗位要求中都有这个“掌握restful框架”。但是在我的认知里,restful好像是一种标准或者是风格啊,并不是某个具体的框架。因为自己也在这块有存疑。所以去网上寻找了一下答案。
看Url就知道要什么
看http method就知道干什么
看http status code就知道结果如何
如何理解这个REST
首先要知道全名:Representational State Transfer。
逐字逐句的理解这三个单词:
-
Representational-------------表现层
资源是一种信息实体,他可以有多种外在表现形式(例如文本可以是.txt.html.xml.json等多种格式)
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式(代表一个具体的html页面),属于"表现层"范畴,而URI(我们需要的真正的资源)应该只代表"资源"的位置。它(我们请求的资源)的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。 -
state ---------状态
-
transfer----------转换
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
尽管我似乎明白了这个URI和状态转换的意义和意思,但是仍然不是“人话版本”。查询了知乎之后发现覃老师(覃超)已经在他的答案中引用了一个非常棒的解释
ivony:URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作
接下来就按照这个人话版本进行对restful的理解。
阮一峰api设计指南:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
如何设计
资源就是网络上的一个实体,或者说是网络上的一个具体信息,我们使用URI(统一资源定位符)指向他,每种资源对应一个特定的URI。所谓的上网,其实就是互联网上一系列的资源互动,调用他的URI
HTTP动词:
-
GET(SELECT):从服务器取出资源(一项或多项)。
-
POST(CREATE):在服务器新建一个资源。
-
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
-
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
-
DELETE(DELETE):从服务器删除资源。
-
HEAD:获取资源的元数据。
-
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
规范样例
- 根地址:
https://example.org/api/v1/*
https://api.example.com/v1/*
- 版本信息:可以放在URL里面,但是也可以用HTTP的header
/api/v1/
- URL使用名词而不是动词,推荐使用复数形式:
GET /products : will return the list of all products
POST /products : will add a product to the collection
GET /products/4 : will retrieve product #4PATCH/
PUT /products/4 : will update product #4
- 资源地址推荐使用嵌套结构
GET /friends/10375923/profile
UPDATE /profile/primaryAddress/city
- 警惕返回结果的大小,设置分页和限制
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
- 使用正确的HTTP status code表示访问状态:https://link.zhihu.com/?target=http%3A//www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID 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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
- 在返回结果用明确易懂的文本,而且适当加入注释
- 关于安全:自己的接口就用https,加上一个key做一次hash放在最后即可。考虑到国情,HTTPS在无线网络里不稳定,可以使用Application Level的加密手段把整个HTTP的payload加密。有兴趣的朋友可以用手机连上电脑的共享Wi-Fi,然后用Charles监听微信的网络请求(发照片或者刷朋友圈)。
注意事项:
API的身份认证应该使用OAuth 2.0框架。
服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
认证机制的坑
https://blog.igevin.info/posts/restful-architecture-in-general/#restful
顺带一提的Django rest framework
Django REST Framework是一个基于Django开发的APP,用于快速搭建REST API
安装
pip3 install djangorestframework
使用
注册app
settings.py
INSTALLED_APPS = [
...
'rest_framework',
]
注册路由
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'users', views.UserInfoViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
]
serializers,form验证以及数据库操作
from rest_framework import serializers
from . import models
class UserInfoSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.UserInfo
# fields = ('id', 'username', 'pwd','ug') # fields = '__all__'
exclude = ('ug',)
depth = 1 # 0<=depth<=10
ViewSet视图函数
from rest_framework import viewsets
from . import models
from . import serializers
# ########### 1. 基本处理方式 ###########
class UserInfoViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
允许用户访问查看和编辑的终端路径API
"""
queryset = models.UserInfo.objects.all().order_by('-id')
serializer_class = serializers.UserInfoSerializer
基于CBV
url配置:
from django.conf.urls import url,include
from django.contrib import admin
from . import views
urlpatterns = [
url(r'^users/$', views.UserList.as_view()),
url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
]
视图函数编写:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser
from . import models
from . import serializers
class UserList(APIView):
def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all()
serializer = serializers.MySerializer(instance=user_list, many=True)
return Response(serializer.data)
def post(self, request, *args, **kwargs):
data = JSONParser().parse(request)
serializer = serializers.MySerializer(data=data)
if serializer.is_valid():
# print(serializer.data)
# print(serializer.errors)
# print(serializer.validated_data)
# 如果有instance,则执行update方法;否则,执行create
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
class UserDetail(APIView):
def get(self, request, *args, **kwargs):
obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
serializer = serializers.MySerializer(obj)
return Response(serializer.data)
def delete(self, request, *args, **kwargs):
obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
obj.delete()
return Response(status=204)
def put(self, request, *args, **kwargs):
data = JSONParser().parse(request)
obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
serializer = serializers.MySerializer(obj, data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=400)
编写seiralizers
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from . import models
class MySerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
username = serializers.CharField(required=False, allow_blank=True, max_length=100)
pwd = serializers.CharField()
def validate_username(self, value):
if value == '中国':
raise ValidationError('用户名中存在敏感字符')
return value
def validate_pwd(self, value):
print(value)
return value
def validate(self, attrs):
print(attrs)
return attrs
def create(self, validated_data):
"""
当执行save方法时,自动调用。instance未传值
:param validated_data:
:return:
"""
print(validated_data)
return models.UserInfo.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
当执行save方法时,自动调用。instance传值
:param instance:
:param validated_data:
:return:
"""
instance.username = validated_data.get('username', instance.username)
instance.save()
return instance
基于FBV
url
from django.conf.urls import url,include
from django.contrib import admin
from . import views
urlpatterns = [
url(r'^users/$', views.user_list),
url(r'^users/(?P<pk>[0-9]+)/$', views.user_detail),
]
视图:
from django.http import JsonResponse,HttpResponse
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
from rest_framework.decorators import api_view
from .serializers import MySerializer
from . import models
@api_view(['GET',"POST"])
def user_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
user_list = models.UserInfo.objects.all()
serializer = MySerializer(user_list,many=True)
return Response(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = MySerializer(data=data)
if serializer.is_valid():
print(serializer.data)
print(serializer.errors)
print(serializer.validated_data)
# 如果有instance,则执行update方法;否则,执行create
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
@api_view(['GET',"POST","PUT"])
def user_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
obj = models.UserInfo.objects.filter(pk=pk).first()
if not obj:
return HttpResponse(status=404)
if request.method == 'GET':
serializer = MySerializer(obj)
# return JsonResponse(serializer.data,json_dumps_params={'ensure_ascii':False},content_type='application/json;charset=utf-8')
return Response(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = MySerializer(obj, data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=400)
elif request.method == 'DELETE':
obj.delete()
return Response(status=204)
编写serializers
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from . import models
class MySerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
username = serializers.CharField(required=False, allow_blank=True, max_length=100)
pwd = serializers.CharField()
def validate_username(self, value):
if value == '中国':
raise ValidationError('用户名中存在敏感字符')
return value
def validate_pwd(self, value):
print(value)
return value
def validate(self, attrs):
print(attrs)
return attrs
def create(self, validated_data):
"""
当执行save方法时,自动调用。instance未传值
:param validated_data:
:return:
"""
print(validated_data)
return models.UserInfo.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
当执行save方法时,自动调用。instance传值
:param instance:
:param validated_data:
:return:
"""
instance.username = validated_data.get('username', instance.username)
instance.save()
return instance
权限控制
settings.py配置
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'permissi.MyPermission',
]
}
权限控制:
class MyPermission(object):
"""
A base class from which all permission classes should inherit.
"""
def has_permission(self, request, view):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
def has_object_permission(self, request, view, obj):
return True