前言
djangorestframework
也叫 drf
,在使用 drf
之前我们需要先知道什么 REST
。
REST(Representational State Transfer,表现层状态转移)是一种软件架构风格,它不是一种标准,也不是一种技术,而是一种设计Web服务的方法。
RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。
了解RESTful
RESTful架构的优点
- 前后端分离:前端负责展示和渲染,后端负责处理数据并以JSON格式传输出去。这样的统一接口在Web、iOS、Android三端都可以使用,减少了重复代码的编写。
- 无状态:每次请求都包含了所有必要的信息,这使得每个请求都是独立的,服务器不需要保存任何客户端的状态,从而更容易实现服务的扩展。
RESTful架构的缺点
- 无状态约束:虽然无状态增加了可扩展性,但对于需要保存用户状态的应用来说,每次请求都需要包含所有信息,可能会导致请求变得复杂。
RESTful架构的约束条件
- 客户端-服务器结构:客户端和服务器之间通过统一的接口进行通信。
- 无状态:服务器不保存任何客户端的状态。
- 可缓存:响应需要被标记为可缓存或不可缓存,以减少客户端和服务器之间的交互。
- 统一接口:系统中的所有交互都是通过统一的接口进行的,这使得系统中的各个部分可以独立地进行演化2。
RESTful架构的实现
在实现RESTful架构时,需要注意以下几点:
- 使用HTTP方法:GET用于获取资源,POST用于创建资源,PUT用于更新资源,DELETE用于删除资源。
- 资源的表述:资源可以有多种表述形式,客户端和服务器通过内容协商来确定资源的表述方式。
- 资源的链接:资源的表述应该包含链接,这些链接指向了资源的操作或相关资源,使得客户端可以通过这些链接进行状态转移。
RESTful规范
十条规范如下:
数据的安全保障
url链接一般都采用https协议进行传输 注:采用https协议,可以提高数据交互过程中的安全性。
接口特征表现,一看就知道是个api接口
- 用api关键字标识接口url: - [https://api.baidu.com](https://api.baidu.com/) - https://www.baidu.com/api 注:看到api字眼,就代表该请求url链接是完成前后台数据交互的
多数据版本共存
- 在url链接中标识数据版本, 场景:比如移动端接口升级,但是不能影响老接口 - https://api.baidu.com/v1 - https://api.baidu.com/v2
数据即是资源,均使用名词(可复数)
- 接口一般都是完成前后台数据的交互,交互的数据我们称之为资源 - https://api.baidu.com/users - https://api.baidu.com/books - https://api.baidu.com/book 注:一般提倡用资源的复数形式,在url链接中奖励不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user - 特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义 - https://api.baidu.com/place/search - https://api.baidu.com/login
资源操作由请求方式决定(method)
- 操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作 - https://api.baidu.com/books - get请求:获取所有书 - https://api.baidu.com/books/1 - get请求:获取主键为1的书 - https://api.baidu.com/books - post请求:新增一本书书 - https://api.baidu.com/books/1 - put请求:整体修改主键为1的书 - https://api.baidu.com/books/1 - patch请求:局部修改主键为1的书 - https://api.baidu.com/books/1 - delete请求:删除主键为1的书
过滤,通过在url上传参的形式传递搜索条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量 - https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置 - https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数 - https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序 - https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
响应状态码
7.1 正常响应 - 响应状态码2xx - 200:常规请求 - 201:创建成功 7.2 重定向响应 - 响应状态码3xx - 301:永久重定向 - 302:暂时重定向 7.3 客户端异常 - 响应状态码4xx - 403:请求无权限 - 404:请求路径不存在 - 405:请求方法不存在 7.4 服务器异常 - 响应状态码5xx - 500:服务器异常
错误处理,应返回错误信息,error当做key
{ error: "无权限操作" }
返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档
需要url请求的资源需要访问资源的请求链接
# Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么 { "status": 0, "msg": "ok", "results":[ { "name":"肯德基(罗餐厅)", "img": "https://image.baidu.com/kfc/001.png" } ... ] }
DjangoRESTframework简单使用
drf官方文档:https://www.django-rest-framework.org/
安装
在安装之前,请确认自己django版本是否支持。我这里使用 djangorestframework==3.15.2
版本,要求如下:
- Django(4.2、5.0)
- Python(3.8、3.9、3.10、3.11、3.12)
使用pip安装:
pip install djangorestframework==3.15.2
简单使用
对于普通写法,使用 drf
时和之前区别不大。只不过 drf
提供了很多便利性的功能。比如认证、权限、频率三大组件。高级用法请看后面的文章。
setting.py配置
# 在setting.py的app中注册
INSTALLED_APPS = [
'......',
'rest_framework',
'django_filters', # 可选,只有使用到filter时才需要配置
]
# 可选配置:根据实际使用情况来使用
REST_FRAMEWORK = {
# 设置全局默认渲染器
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer', # json格式展示响应
'rest_framework.renderers.BrowsableAPIRenderer', # 网页渲染格式展示响应(rest默认渲染)
]
# 配置全局认证
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication', # rest默认验证
'rest_framework.authentication.SessionAuthentication', # rest默认验证
'app01.app_auth.MyAuthentication', # 自定义认证类
],
# 配置全局权限
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', # rest默认权限
'app01.app_auth.UserPermission', # 自定义权限
],
# 配置全局访问频率
'DEFAULT_THROTTLE_CLASSES': [ # 访问频率策略
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': { # 访问频率速率:值包括second、minute、hour或day作为节流周期。
'anon': '100/day',
'user': '1000/day'
},
# 配置全局过滤器,需要安装`pip3 install django-filter`,且在app中注册
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend'
]
}
视图使用
from rest_framework.views import APIView
from rest_framework.response import Response
class BookView(APIView):
def get(self, request):
ret = {
"data": [{
"name": "djangorestframework基础使用",
"author": "wuye",
"price": "66.66"
},{
"name": "djangorestframework基础使用(二)",
"author": "wuye",
"price": "66.66"
}]
}
return Response(ret)
这就是最简单的使用了。
djangorestframework源码分析
cbv源码
# 先读View的源码: Django==4.2.16
from django.views import View
# urls.py
path('books1/', views.Books.as_view()), #在这个地方应该写个函数内存地址,views.Books.as_view()执行完,是个函数内存地址,as_view是一个类方法,类直接来调用,会把类自动传入
# 放了一个view的内存地址(View--》as_view--》内层函数)
# 请求来了,如果路径匹配,会执行:函数内存地址(request), 返回结果又调用了self.dispatch()
class View:
def as_view(cls, **initkwargs)
pass
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
pass
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(
self, request.method.lower(), self.http_method_not_allowed
)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
APIView源码分析
#from rest_framework.views import APIView
# urls.py
path('booksapiview/', views.BooksAPIView.as_view()), #在这个地方应该写个函数内存地址
class APIView(View):
#APIView的as_view方法(类的绑定方法)
@classmethod
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs) # 调用父类(View)的as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# 以后所有的请求,都没有csrf认证了,只要继承了APIView,就没有csrf的认证
return csrf_exempt(view)
#请求来了---》路由匹配上---》view(request)---》调用了self.dispatch(),会执行apiview的dispatch
# APIView的dispatch方法
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 重新包装成一个request对象,以后再用的request对象,就是新的request对象了
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 三大认证模块
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 响应模块
response = handler(request, *args, **kwargs)
except Exception as exc:
# 异常模块
response = self.handle_exception(exc)
# 渲染模块
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
# APIView的initial方法
def initial(self, request, *args, **kwargs):
# 认证组件:校验用户 - 游客、合法用户、非法用户
# 游客:代表校验通过,直接进入下一步校验(权限校验)
# 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
# 非法用户:代表校验失败,抛出异常,返回403权限异常结果
self.perform_authentication(request)
# 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
# 认证通过:可以进入下一步校验(频率认证)
# 认证失败:抛出异常,返回403权限异常结果
self.check_permissions(request)
# 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
# 没有达到限次:正常访问接口
# 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
self.check_throttles(request)