1,认识RESTful
在前后端分离模式,后端API接口如何定义?
- 后端数据库中保存了商品的信息,前端可能需要对商品数据进行增删改查,那相应的每个操作后端都需要提供一个API接口:
- POST/add-goods 增加商品
- POST /delete-goods 删除商品
- POST /update-goods 修改商品
- GET /get-goods 查询商品信息
- 普遍采用的API的RESTful设计风格
REST
-
即Representational State Transfer的缩写。维基百科称其为“具象状态传输”,国内大部分人理解为“表现层状态转化”。
-
RESTful
是一种开发理念。维基百科说:REST是设计风格而不是标准。 REST描述的是在网络中client和server的一种交互形式;REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口),一种万维网软件架构风格。 -
REST特点:url简洁,将参数通过url传到服务器
-
综合上面的解释,RESTful架构就是:
- 每一个URL代表一种资源;
- 客户端和服务器之间,传递这种资源的某种表现层;
- 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
2,RESTful设计方法
-
(1) 域名
-
应该尽量将API部署在专用域名之下。
-
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
-
(2) 版本(Versioning)
- 应该将API的版本号放入URL。
http://www.example.com/app/1.0/foo
- 另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观
Accept: vnd.example-com.foo+json; version=1.0
- 应该将API的版本号放入URL。
-
(3) 路径
-
资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。
/getProducts
、/listOrders
、/retreiveClientByOrder?orderId=1
-
对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作
GET /products :将返回所有产品清单
POST /products :将产品新建到集合
GET /products/4 :将获取产品 4
PATCH(或)PUT /products/4 :将更新产品 4
- API中的名词应该使用复数。无论子资源或者所有资源
- 举例来说,获取产品的API可以这样定义
获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products
- (4) HTTP动词
-
对于资源的具体操作类型,由HTTP动词表示。
-
常用的HTTP动词有下面四个(括号里是对应的SQL命令)。
-
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
DELETE(DELETE):从服务器删除资源。
# 例如
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:删除某个指定动物园的指定动物
3,使用Django开发REST 接口
- 在Django框架中使用的图书英雄案例来写一套支持图书数据增删改查的REST API接口,来理解REST API的开发
# views.py
from datetime import datetime
class BooksAPIVIew(View):
"""
查询所有图书、增加图书
"""
def get(self, request):
"""
查询所有图书
路由:GET /books/
"""
queryset = BookInfo.objects.all()
book_list = []
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
4,明确REST接口开发的核心任务
- 将程序中的一个数据类型转换为其他格式(字典,json,XML等),例如将Django中模型类对象转换为json字符串,这个转换过程称为序列化
queryset = BookInfo.objects.all()
book_list = []
# 序列化
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
- 反之,将其他格式转换为程序中的数据,例如将json字符串转换为Django中的模型类对象,这个过程称为反序列化
json_bytes = request.body
json_str = json_bytes.decode()
# 反序列化
book_dict = json.loads(json_str)
book = BookInfo.objects.create(
btitle=book_dict.get('btitle'),
bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
)
- 在开发REST API时,视图中要频繁的进行序列化与反序列化的编写
- 在开发 REST API接口时,视图中需要做的核心的事是:
- 将数据库序列化为前端所需要的格式,并返回
- 将前端发动的数据反序列化为数据模型对象 ,并保存到数据库中
5,REST framework Request 与 Response
1. Request
常用属性
.data
-reuest.data
返回解析之后的请求数据,类似于Django中标准的request.POST
和request.FILES
属性
- 包含了解析之后的文件和非文件数据
- 包含了对POST、PUT、PATCH请求解析后的数据
- 利用了REST framework
的parses
解析器,不仅支持表单类型数据,也支持JSON
数据.query_params
request.query_params
与Django标准的request.GET
相同,只是换了名字
2,Response
rest_framework.response.Response
- REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型
- REST framework提供了Renderer 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
构造方式
Response(data, status=None, template_name=None, headers=None, content_type=None)
6,视图
REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。
1. 两个基类
1)APIView rest_framework.views.APIView
- APIView是REST framework提供的所有视图的基类,继承自Django的
View
父类。 APIView
与View
的不同之处在于:- 传入到视图方法中的是REST framework的
Reques
t对象,而不是Django的HttpRequeset对象; - 视图方法可以返回REST framework的
Response
对象,视图会为响应数据设置(render)符合前端要求的格式; - 任何
APIException
异常都会被捕获到,并且处理成合适的响应信息; - 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
- 传入到视图方法中的是REST framework的
2)GenericAPIView rest_framework.generics.GenericAPIView
- 继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin扩展类。
2. 五个扩展类
1)ListModelMixin
-
列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。
-
该Mixin的list方法会对数据进行过滤和分页。
2)CreateModelMixin
- 创建视图扩展类,提供
create(request, *args, **kwargs)
方法快速实现创建资源的视图,成功返回201状态码。 - 如果序列化器对前端发送的数据验证失败,返回400错误。
3) RetrieveModelMixin
- 详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。
- 如果存在,返回200, 否则返回404。
4)UpdateModelMixin
- 更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
- 同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。
- 成功返回200,序列化器校验数据失败时,返回400错误。
5)DestroyModelMixin
- 删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。
- 成功返回204,不存在返回404。
3. 几个可用子类视图
1) CreateAPIView
- 提供 post 方法
- 继承自: GenericAPIView、CreateModelMixin
2)ListAPIView
- 提供 get 方法
- 继承自:GenericAPIView、ListModelMixin
3)RetireveAPIView
- 提供 get 方法
- 继承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
- 提供 delete 方法
- 继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
- 提供 put 和 patch 方法
- 继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
- 提供 get、put、patch方法
7)RetrieveUpdateDestoryAPIView
- 提供 get、put、patch、delete方法
- 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
4. 视图集ViewSet
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
-
list() 提供一组数据
-
retrieve() 提供单个数据
-
create() 创建数据
-
update() 保存数据
-
destory() 删除数据
-
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。
-
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:
class BookInfoViewSet(viewsets.ViewSet):
def list(self, request):
...
def retrieve(self, request, pk=None):
...
- 在设置路由时,我们可以如下操作
urlpatterns = [
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]
action属性
-
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。
-
例如:
def get_serializer_class(self):
if self.action == 'create':
return OrderCommitSerializer
else:
return OrderDataSerializer
常用视图集父类
1) ViewSet
-
继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
-
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
2)GenericViewSet
- 继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。
3)ModelViewSet
- 继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
4)ReadOnlyModelViewSet
- 继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。
路由Routers
-
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。
-
REST framework提供了两个router
SimpleRouter
DefaultRouter
7,其他功能
认证Authentication
-
认证失败会有两种可能的返回值:
- 401 Unauthorized 未认证
- 403 Permission Denied 权限被禁止
权限Permissions
-
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
- 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
- 在通过get_object()获取具体对象时,会进行对象访问权限的判断
限流Throttling
- 可以对接口访问的频次进行限制,以减轻服务器压力。
过滤Filtering
-
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。
-
pip insall django-filter
排序
- 对于列表数据,REST framework提供了
OrderingFilter
过滤器来帮助我们快速指明数据按照指定字段进行排序。
分页Pagination
- REST framework提供了分页的支持。
版本Versioning
- REST framework提供了版本号的支持。
- 在需要获取请求的版本号时,可以通过request.version来获取。
- 默认版本功能未开启,request.version 返回None。
- 开启版本支持功能,需要在配置文件中设置DEFAULT_VERSIONING_CLASS
异常处理 Exceptions
-
REST framework提供了异常处理,可以自定义异常处理函数。
-
REST framework定义的异常
- APIException 所有异常的父类
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- PermissionDenied 权限决绝
- NotFound 未找到
- MethodNotAllowed 请求方式不支持
- NotAcceptable 要获取的数据格式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
自动生成接口文档
-
REST framework可以自动帮助我们生成接口文档。
-
接口文档以网页的方式呈现。
-
自动接口文档能生成的是继承自APIView及其子类的视图。