自动为Flask写的API生成帮助文档

Flask是Python一个非常轻量的库,可以让你毫不费力地写一个简单的网站。如果你需要写一些后台API或者准备自动化测试数据时,Flask是一个非常不错的选择。

一个API例子

举个例子,我们可以这样写几个API,具体实现暂时略过:

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
27
# views/api.py

api = Blueprint('api', __name__)

@api.route('/get_todo', methods=['GET'])
def get_todo():
"""Get all todo tasks."""
pass


@api.route('/add_todo', methods=['POST'])
def add_todo():
"""
Add a todo task, please post data in json format, e.g.

data = {
"name":"the title",
"task":"the detail"
}
"""
pass


@api.route('/delete_todo', methods=['GET', 'POST'])
def delete_todo():
"""Delete a todo task."""
pass

一旦你的API完成,你可能需要和调用方沟通调用的细节,最好给一些例子。明明你已经在代码里给所有方法都写了注释,难道还要再把这些注释拿出来重新组织排版一下?

我猜你和我一样,听过这么一句话。

read the fucking manual!

可是谁会去翻代码去看你的注释呢,何况你的代码他们还不一定能看到。如果能自动生成一个帮助页面那就好了。

自动化API帮助文档

假设我们的API都是以 http://127.0.0.1/api/* 的形式书写的,那么最好把API的完整列表就放在根目录下面,比如这样:

api-demo-home

view 方法的实现主要依靠 app.url_map 来获取Flask中所有的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# views/api.py

def get_api_map():
"""Search API from rules, if match the pattern then we said it is API."""
for rule in get_app().url_map.iter_rules():
if re.search(r'/api/.+', str(rule)):
yield str(rule), rule.endpoint


@api.route('/', methods=['GET'])
def index():
"""List all API to this page, api_map contains each api url + endpoint."""
api_map = sorted(list(get_api_map()))
index_url = url_for('main.index', _external=True)
api_map = [(index_url + x[0][1:], x[1]) for x in api_map]
return render_template('api_index.html', api_map=api_map)

模板的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# templates/api_index.html

{% extends "./layout.html" %}

{% block title %}API Root{% endblock %}

{% block breadcrumb_nav %}
<li><a href="{{ url_for('api.index') }}">Api Root</a></li>
{% endblock %}

{% block page_header %}
<h1>Api Root</h1>
{% endblock %}

{% block content_area %}
<pre>{
{% for i in api_map %} "<a href="/docs/{{ i[1] }}">{{ i[0] }}</a>"{{ ",\n" if not loop.last }}{% endfor %}
}</pre>
{% endblock %}

接下来我们来文档化每个具体的API方法,最终的展示结果会是这样的。

api-demo-full

view 方法的实现思路其实也很明确,我们可以通过 app.view_functions 这个字典找到每个API 的endpoint所绑定的方法,然后访问方法的名字和文档即可。

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# views/main.py

main = Blueprint('main', __name__)


@main.route('/', methods=['GET'])
def index():
"""Redirect home page to docs page."""
return redirect(url_for('api.index'))


@main.route('/docs/<endpoint>', methods=['GET'])
def docs(endpoint):
"""Document page for an endpoint."""
api = {
'endpoint': endpoint,
'methods': [],
'doc': '',
'url': '',
'name': ''
}

try:
func = get_app().view_functions[endpoint]

api['name'] = _get_api_name(func)
api['doc'] = _get_api_doc(func)

for rule in get_app().url_map.iter_rules():
if rule.endpoint == endpoint:
api['methods'] = ','.join(rule.methods)
api['url'] = str(rule)

except:
api['doc'] = 'Invalid api endpoint: "{}"!'.format(endpoint)

return render_template('api_docs.html', api=api)


def _get_api_name(func):
"""e.g. Convert 'do_work' to 'Do Work'"""
words = func.__name__.split('_')
words = [w.capitalize() for w in words]
return ' '.join(words)


def _get_api_doc(func):
if func.__doc__:
return func.__doc__
else:
return 'No doc found for this API!'

模板的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{% extends "./layout.html" %}

{% block title %}API - {{ api['name'] }}{% endblock %}

{% block breadcrumb_nav %}
<li><a href="{{ url_for('api.index') }}">Api Root</a></li>
<li><a href="{{ api['url'] }}">{{ api['name'] }}</a></li>
{% endblock %}

{% block page_header %}
<h1>{{ api['name'] | upper }}</h1>
{% endblock %}

{% block content_area %}
<pre>
<b>Target:</b><span><a href="{{ api['url'] }}">{{ api['url'] }}</a></span>
<b>Allow :</b> <span>{{ api['methods'] }}</span>
<b>Usage :</b> <span>{{ api['doc'] }}</span>
</pre>
{% endblock %}

GitHub项目地址

如果你想看完整的例子,可以到我的GitHub去拉一份代码。

https://github.com/tobyqin/flask_api_doc

只需要三步就可以在你的机器上运行Demo:

1
2
3
cd /path/to/flask_api/doc
pip install -r requirements.txt
python main.py

如果你觉得Demo不错,欢迎给个Star。有建议或者想法也可以拿来讨论。

Toby Qin wechat
欢迎到微信里去当吃瓜群众