1. 为什么需要修改DRF-YASG的API文档页面数据
在Django REST framework项目中,自动生成的API文档是开发者与前端团队沟通的重要桥梁。DRF-YASG作为目前最流行的DRF文档生成工具,其默认提供的Swagger/Redoc界面虽然功能完善,但实际项目中我们经常遇到需要定制化文档界面的场景:
- 公司品牌视觉规范要求统一文档页面的配色和LOGO
- 需要隐藏某些敏感接口或字段(如内部调试接口)
- 文档默认展示的字段顺序不符合业务逻辑
- 要为不同权限的用户展示差异化的文档内容
- 需要在文档中添加自定义的使用说明或示例
最近我在为一家金融科技公司实施API网关时,就遇到了必须深度定制文档界面的需求。他们的安全团队要求:
- 移除Swagger默认的"Try it out"按钮防止生产环境误操作
- 在文档头部添加JWT认证的使用指引
- 修改响应示例中的测试数据为符合金融规范的数据格式
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. DRF-YASG的配置体系解析
2.1 基础配置项说明
在settings.py中,DRF-YASG通过SWAGGER_SETTINGS字典提供配置入口。常用配置项包括:
python复制SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
}
},
'USE_SESSION_AUTH': False,
'JSON_EDITOR': True,
'OPERATIONS_SORTER': 'alpha',
'TAGS_SORTER': 'alpha',
'DOC_EXPANSION': 'none',
}
但以上配置主要针对文档功能,对界面数据的修改能力有限。要实现深度定制,需要理解DRF-YASG的三层架构:
- Schema生成层:由
yasg.AutoSchema处理,负责从DRF的ViewSets生成OpenAPI规范 - 模板渲染层:基于Django模板系统,控制HTML页面的生成
- 前端静态资源:Swagger-UI/Redoc的JavaScript和CSS资源
2.2 修改入口定位技巧
通过分析源码发现,界面数据修改主要有三个切入点:
- Schema转换器:通过继承
OpenAPISchemaGenerator重写get_schema - 模板上下文:自定义视图类修改
get_swagger_view的extra_context - 前端覆盖:替换静态资源或通过JavaScript运行时修改
提示:在Django 3.0+项目中,静态资源会被存入
_static目录,直接修改可能导致更新失效,推荐使用方法1或2
3. 实战:五种常见修改场景的实现
3.1 场景一:修改文档标题和描述
创建自定义Schema生成器:
python复制# schemas.py
from drf_yasg.generators import OpenAPISchemaGenerator
class CustomOpenAPISchemaGenerator(OpenAPISchemaGenerator):
def get_schema(self, request=None, public=False):
schema = super().get_schema(request, public)
schema.info.title = "金融支付网关API"
schema.info.description = """
## 使用须知
1. 所有请求必须携带JWT认证头
2. 金额字段单位为分(整数)
3. 交易时间格式: YYYY-MM-DD HH:MM:SS
"""
return schema
在urls.py中应用:
python复制from drf_yasg.views import get_schema_view
from .schemas import CustomOpenAPISchemaGenerator
schema_view = get_schema_view(
generator_class=CustomOpenAPISchemaGenerator,
public=True,
)
urlpatterns = [
path('docs/', schema_view.with_ui('swagger'), name='schema-swagger-ui'),
]
3.2 场景二:隐藏特定接口
通过操作tags和operationId实现接口过滤:
python复制# filters.py
from drf_yasg.inspectors import SwaggerAutoSchema
class CustomSwaggerAutoSchema(SwaggerAutoSchema):
def get_tags(self, operation_keys=None):
tags = super().get_tags(operation_keys)
if 'internal' in tags:
return None # 隐藏标记为internal的接口
return tags
# settings.py
SWAGGER_SETTINGS = {
'DEFAULT_AUTO_SCHEMA_CLASS': 'api.filters.CustomSwaggerAutoSchema'
}
3.3 场景三:修改字段示例值
使用FieldInspector干预字段生成:
python复制# inspectors.py
from drf_yasg.inspectors import FieldInspector
class ExampleModifierInspector(FieldInspector):
def process_result(self, result, method_name, obj, **kwargs):
if isinstance(result, openapi.Schema):
if result.type == 'string' and result.format == 'date-time':
result.example = '2023-01-01 12:00:00'
elif result.type == 'integer' and hasattr(obj, 'help_text'):
if '金额' in obj.help_text:
result.example = 100 # 默认示例金额1元
return result
# 在View中使用
@swagger_auto_schema(
field_inspectors=[ExampleModifierInspector]
)
def post(self, request):
...
3.4 场景四:添加自定义CSS/JS
创建模板继承文件templates/drf-yasg/swagger-ui.html:
django复制{% extends "drf-yasg/swagger-ui.html" %}
{% block extra_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// 移除Try it out按钮
const tryItOutButtons = document.getElementsByClassName('try-out__btn');
for (let btn of tryItOutButtons) {
btn.remove();
}
// 添加自定义横幅
const header = document.querySelector('.swagger-ui .topbar');
if (header) {
const banner = document.createElement('div');
banner.innerHTML = '<div style="padding:10px;background:#f5f5f5;border-bottom:1px solid #ddd">生产环境API文档 - 只读模式</div>';
header.prepend(banner);
}
});
</script>
{% endblock %}
{% block extra_styles %}
<style>
.opblock-summary-path {
font-weight: bold;
color: #3b4151;
}
.model-box {
background-color: #fafafa;
}
</style>
{% endblock %}
3.5 场景五:动态权限控制
实现按用户角色显示不同文档内容:
python复制# permissions.py
from rest_framework.permissions import BasePermission
from drf_yasg.views import get_schema_view
class APIDocPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_superuser:
return True
return 'api.view_docs' in request.user.get_all_permissions()
# urls.py
schema_view = get_schema_view(
permission_classes=[APIDocPermission],
)
# 在生成器中过滤接口
class RoleBasedSchemaGenerator(OpenAPISchemaGenerator):
def get_endpoints(self, request):
endpoints = super().get_endpoints(request)
if not request.user.is_superuser:
return {
path: path_info
for path, path_info in endpoints.items()
if not path.startswith('/internal/')
}
return endpoints
4. 高级定制技巧与性能优化
4.1 缓存策略优化
文档生成可能成为性能瓶颈,推荐采用两级缓存:
python复制from django.core.cache import caches
from drf_yasg.generators import OpenAPISchemaGenerator
class CachedSchemaGenerator(OpenAPISchemaGenerator):
cache_timeout = 60 * 60 * 24 # 24小时
def get_schema(self, request=None, public=False):
cache_key = f'schema_{public}_{request.user.pk if request else "anon"}'
schema = caches['default'].get(cache_key)
if not schema:
schema = super().get_schema(request, public)
caches['default'].set(cache_key, schema, self.cache_timeout)
return schema
4.2 自动化Mock响应
通过扩展Schema实现一键生成Mock数据:
python复制from faker import Faker
class MockSchemaGenerator(OpenAPISchemaGenerator):
def get_schema(self, request=None, public=False):
schema = super().get_schema(request, public)
fake = Faker()
for path in schema.paths.values():
for operation in path.operations.values():
if not operation.responses:
continue
for response in operation.responses.values():
if not response.schema:
continue
# 为每个schema添加mock示例
self._add_mock_examples(response.schema, fake)
return schema
def _add_mock_examples(self, schema, fake):
if hasattr(schema, 'properties'):
example = {}
for name, prop in schema.properties.items():
if prop.type == 'string':
if prop.format == 'date-time':
example[name] = fake.date_time_this_year().isoformat()
else:
example[name] = fake.word()
elif prop.type == 'integer':
example[name] = fake.random_number(digits=3)
# 其他类型处理...
schema.example = example
4.3 文档版本控制方案
实现多版本API文档共存:
python复制# urls.py
from django.urls import path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
v1_schema_view = get_schema_view(
openapi.Info(
title="API V1",
default_version='v1',
),
patterns=[path('api/v1/', include('api.v1.urls'))],
)
v2_schema_view = get_schema_view(
openapi.Info(
title="API V2",
default_version='v2',
),
patterns=[path('api/v2/', include('api.v2.urls'))],
)
urlpatterns = [
path('docs/v1/', v1_schema_view.with_ui('swagger')),
path('docs/v2/', v2_schema_view.with_ui('swagger')),
]
5. 生产环境最佳实践
5.1 安全加固措施
-
访问控制:
python复制# settings.py SWAGGER_SETTINGS = { 'VALIDATOR_URL': None, # 禁用在线验证 'LOGIN_URL': 'admin:login', 'LOGOUT_URL': 'admin:logout', } -
敏感信息过滤:
python复制class SecurityInspector(FieldInspector): def process_result(self, result, method_name, obj, **kwargs): if isinstance(result, openapi.Schema): if hasattr(obj, 'field_name'): if obj.field_name.lower() in ['password', 'secret']: result.example = '******' result.description = (result.description or '') + '\n\n**SENSITIVE FIELD**' return result
5.2 性能监控指标
添加文档访问监控:
python复制# middleware.py
from django.utils.deprecation import MiddlewareMixin
class DocsMetricsMiddleware(MiddlewareMixin):
def process_view(self, request, view_func, view_args, view_kwargs):
if 'schema-swagger-ui' in view_func.__name__:
start_time = time.time()
response = view_func(request, *view_args, **view_kwargs)
duration = time.time() - start_time
statsd.timing('api.docs.render_time', duration * 1000)
statsd.incr('api.docs.access_count')
return response
return None
5.3 CI/CD集成方案
在部署流程中添加文档校验:
yaml复制# .gitlab-ci.yml
validate_schema:
stage: test
image: python:3.9
script:
- pip install -r requirements.txt
- python manage.py generate_swagger -f yaml > openapi.yaml
- swagger-cli validate openapi.yaml
rules:
- changes:
- api/**/*
- docs/**/*
我在金融项目中的实际经验表明,完善的文档系统可以减少80%以上的接口沟通成本。但要注意的是,过度定制会增加维护负担,建议根据团队规模平衡灵活性和可维护性。一个实用的技巧是建立文档变更日志,记录每次修改的内容和原因,这对后续升级DRF-YASG版本特别有帮助。
