DRF整合了非常多的开发常用组件,认证,授权、限流等等,简单配置即可使用

认证和授权

全局配置

开启全局配置,只要请求后端接口,都需要进行认证才可以请求成功

settings.py

1
2
3
4
5
6
7
8
9
10
11
12
13
# DRF 组件配置
REST_FRAMEWORK = {
# 内部循环遍历注册的认证类,一旦成功,结束循环
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'dfdemo.authentication.CustomAuthentication', # 自定义认证
'rest_framework.authentication.SessionAuthentication', # session 认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
),
# 权限配置
'DEFAULT_PERMISSION_CLASSES': (
# 要认证才能请求
'rest_framework.permissions.IsAuthenticated',
),

我们再去请求后端接口,在没有登录的情况下是无法请求成功的

登录管理员后,再去请求就成功了,获得了登录信息

但是,我们并不是后端所有接口都需要认证,比如在登录中的登录接口和验证码接口就不能要求认证,不然就是先有鸡先有蛋的问题出现了,这时候我们可以进行局部认证,让这两个接口不需要进行认证

局部配置

1
2
3
4
# 局部认证
# 登录接口,要取消所有的认证与权限规则,也就是要做局部禁用操作(空配置)
authentication_classes = []
permission_classes = []

此时我们再去请求不需要做认证的接口

发现,没有进行登录也可以访问,访问没有做局部配置的接口,依旧需要认证

除了需要登录这个认证,部分接口是只有管理员才能访问的,所以我们对管理员接口进行局部配置,即使登陆了,不是管理员也不能访问

1
2
3
# 局部权限配置方式 管理员
authentication_classes = [SessionAuthentication]
permission_classes = [IsAdminUser]

创建的csq账号,是没有管理员权限的,所以即使是登录也不能请求成功这个接口

换做luffy就可以了

还可以自定义一个认证类,实现一个接口的自定义认证,如下

authentication.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CustomAuthentication(BaseAuthentication):
'''
自定义认证
'''
def authenticate(self, request):
'''
认证方法
:param request: 客户端发来的http对象
:return:
'''
user = request.query_params.get('user')
pwd = request.query_params.get('pwd')
if user != 'root' or pwd != '123456':
return None
# get_user_model 获取当前系统中对应的用户模型类
print('调用用户自定义权限类')
user = get_user_model().objects.first()
# 按照固定的返回格式填写(用户模型,None)
return (user,None)

写的很简单,就是要再地址栏拼接上用户名和密码

目前做了全局认证和权限配置,必须登录能访问接口,现在我们把自定义的配置传到接口中测试,测试在未登录的情况下拼接地址能不能请求成功。

发现请求成功,我们不这样拼接,直接登录超级管理员登录后,去请求该接口

发现请求失败,我们在PyCharm中也发现打印信息:

在全局配置中,配置的授权信息是认证就可以访问,现在我们可以自定义权限配置

permission.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class IsXiaoMingPermissions(BasePermission):
'''
自定义权限,可全局,也可局部
'''
def has_permission(self, request, view):
'''
试图权限 True 可以访问
:param request:
:param view:
:return:
'''
role = request.query_params.get('role')
return role == 'xiaoming'


def has_object_permission(self, request, view, obj):
'''
模型权限
:param request:
:param view:
:param obj:
:return:
'''
return True

view.py

1
permission_classes = [IsXiaoMingPermissions] # 自定义权限

这样的局部配置完成后,要求请求这个类的接口都要符合 IsXiaoMingPermissions.py,否则即使是超级管理员也无法请求,测试如下:

vyND5n.png

用luffy这个超级管理员账户测试

即使登录的是超级管理员,也无法请求成功!必须是通过自定义的授权程序才能请求成功。

总结

  1. 全局配置决定了程序所有接口的认证和授权信息,但可以通过局部配置进行覆盖,生效的就是局部配置了
  2. 认证和授权联系紧密,在配置的时候要考虑两者之间的关系

限流

全局配置

settings.py

1
2
3
4
5
6
7
8
9
10
11
# 节流的配置列表
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 匿名未认证用户,使用IP区分用户
'rest_framework.throttling.UserRateThrottle' # 使用User id 来区分
# 'rest_framework.throttling.ScopeRateThrottle', # 特定域限流
),
# 节流率
'DEFAULT_THROTTLE_RATES': {
'anon': '3/day', # 匿名用户每天三次
'user': '5/day', # 登录用户每天五次
},

未认证用户使用IP区分,已认证用户使用ID用于生成唯一的密钥以进行限制 。

节流率的时间可以用天,小时,分,秒等来进行限定

分别测试未登录和登录用户去请求

达到请求上限,得到预期效果

局部配置

假设全局并没有做限流操作,只是在某个接口中需要做限流,可以采用局部限流的方法,做法有两种

取消全局限流,保留限流率,对需要做限流的操作进行局部限流处理

1
2
3
4
5
class StudentReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# 局部限流
throttle_classes = [AnonRateThrottle,UserRateThrottle]

发现配置了局部限流的接口会受限流率的操作,没有配置的接口则不受限制

第二种方法是,在全局仍需限流操作的情况下,部分接口与全局限流率不一致,这是需要配置局部限流率了

1
2
3
4
5
6
7
8
9
10
11
12
# 节流的配置列表
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 匿名未认证用户,使用IP区分用户
'rest_framework.throttling.UserRateThrottle' # 使用User id 来区分
# 'rest_framework.throttling.ScopeRateThrottle', # 自定义限流域
),
# 节流率
'DEFAULT_THROTTLE_RATES': {
'anon': '3/day', # 匿名用户每天三次
'user': '5/day', # 登录用户每天五次
'Hom': '10/day',
},

局部限流配置10次/day,全局限流配置登录用户3次/day

view.py

1
2
3
4
5
6
7
8
from rest_framework.throttling import ScopedRateThrottle
class StudentReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer

# 局部限流 10/day
throttle_classes = [ScopedRateThrottle]
throttle_scope = 'Hom'

过滤

settings.py

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
....
'django_filters',# 注册主键
...
]

# 过滤查询,全局配置[过滤和排序公用一个配置项]
'DEFAULT_FILTER_BACKENDS':
['django_filters.rest_framework.DjangoFilterBackend'],

view.py

1
filter_fields = ['sex','classmate']

简单的配置就是这样,全局开启过滤配置,具备设置过滤字段,就可以发送带参过滤Get请求了

还有很多强大的配置,等到时候工作用到了再去文档里看

排序

setting.py

1
2
3
4
5
# 过滤查询,全局配置[过滤和排序公用一个配置项]
'DEFAULT_FILTER_BACKENDS':[
'django_filters.rest_framework.DjangoFilterBackend', # 过滤
'rest_framework.filters.OrderingFilter', # 排序
],

view.py

1
2
3
4
# 局部配置排序
# 请求格式 ordering=-id - 表示倒序
# 多字段排序 ?ordering = -id,age 多字段排序
ordering_fields = ['id','age']

分页

全局配置

settings.py

1
2
3
4
5
6
# 全局配置分页
# 页码分页器
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # page=页码&page_size=单页数据量
# 偏移量分页器
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', # limit=单页数据量&offset=页码
'PAGE_SIZE': 3,

这样配置,视图层只要是返回list就会分页

但是通常不会设置全局分页,因为有很多业务是不需要分页的,我们需要配置局部分页

局部配置

局部配置就是自己写个自己的分页器,然后在需要进行分页的时候引入即可

pagiation.py

1
2
3
4
5
class StudentPageNumberPagination(PageNumberPagination):
page_size = 10 # 默认分页的每一页数据量
max_page_size = 20 # 客户端可调整最大单页数据量
page_query_param = 'page' # 页码
page_size_query_param = 'page_size' # 单页数据量

view.py中引入自定义分页器

1
2
3
# 分页器局部分页
# 声明自定义的分页配置类
pagination_class = StudentPageNumberPagination

注意:如果在视图内关闭分页功能,只需在视图内设置

1
pagination_class = None

异常处理

DRF的 Respone已经给我们封装了很多错误信息,比如下面限流的

[

但是还是会有很多异常不能处理返回,一旦没有处理就之间返回错误页面,对前端是很不友好的,比如我们做一个除0操作

我们要对异常进行处理,就要自己去写个类,去处理 Response无法处理的异常

execeptions.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from rest_framework.views import exception_handler
from rest_framework import status
from rest_framework.response import Response
from django.db import DatabaseError
def custom_exception_handle(exc,context):
'''
自定义异常函数 要注册到setting中

:param exc: 异常对象
:param context: 上下文,字典
:return:
'''
# 先让drf处理
response = exception_handler(exc,context)
# drf未识别的再自己处理
if response is None:
view = context['view']
request = context['request']
if isinstance(exc,DatabaseError):
print('[%s]:%s'%(view,exc))
response = Response({'detail':'服务器内部错误'},status=status.HTTP_507_INSUFFICIENT_STORAGE)
elif isinstance(exc,ZeroDivisionError):
response = Response({'detail': '算术异常'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
response = Response({'detail': '未捕获异常'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return response

这个异常处理类最好写在父组件下,因为所有组件都需要这个全局异常处理

配置自定义异常处理 setting.py

1
2
# 自定义异常处理
'EXCEPTION_HANDLER': 'drfdemo.exceptions.custom_exception_handle',

测试效果

自动生成接口文档

coreapi

需要先下载库

1
pip install coreapi

settings.py

1
2
# 自动生成接口文档
'DEFAULT_SCHEMA_CLASS':'rest_framework.schemas.AutoSchema',

urls.py

1
path('docs/',include_docs_urls(title='API文档')),

浏览器访问http://127.0.0.1:8000/docs/,即可看到接口文档了,还可以进行测试

swagger

SpringBoot常用的API接口文档就是Swagger,DRF也可以整合Swagger

下载库

1
pip install drf_yasg

添加组件

1
2
3
4
5
6
INSTALLED_APPS = [
...
'drf_yasg',
...
]

swagger_setting.py

1
2
3
4
5
6
7
8
9
10
11
12
schema_view = get_schema_view(
openapi.Info(
title="接口文档平台", # 必传
default_version='v1', # 必传
description="文档描述",
terms_of_service='',
contact=openapi.Contact(email="389783961@qq.com"),
license=openapi.License(name="BSD LICENSE")
),
public=True,
# permission_classes=(rest_framework.permissions.AllowAny) # 权限类
)

urls.py

1
2
3
4
5
6
7
8
9
urlpatterns = [
path('admin/', admin.site.urls),
path('stu/', include('students.urls')),
path('req/', include('req.urls')),
# path('docs/',include_docs_urls(title='API文档')),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]

浏览器访问http://127.0.0.1:8000/swagger/


DRF系列总结

作为Django的插件,DRF对API的封装非常完美,但是有时候又显得很鸡肋,不够相比它带给我们的便利,就不值一提了。

drfdemo源码

DRF中文官方文档

2022年3月27日10点06分完结!