1.flask restful web service
2.flask restful api(待更新)
3.flask httpauth实现权限管控(待更新)
4.uwsgi管理flask应用(待更新)
文章摘自http://www.pythondoc.com/flask-restful/
并由本人修正其中的一些bug及版本更新导致的程序问题,去其糟粕,取其精华。
flask是当下python比较流行的web开发框架,遂在使用flask做了大大小小的项目之后,便想着做一篇博客,一方面以用来记录关键性的知识,另一方面也便以那些想入门或者想了解python、flask的人。
----
本文需要读者有一定的python基础
----
- 一个flask小程序(使用pycharm开发)
from flask import Flask
app = Flask(__name__)
@app.route(‘/‘)
def index():
return ‘hello world‘
if __name__ == (__main__):
app.run(debug=True)
如上一些参数的说明:
- from flask import Flask 引入flask中的Flask类模块
-
app = Flask(__name__)
实例化Flask类 - @app.route(‘/‘) flask路由注释器
?
运行如上的程序便可以启动一个flask程序,此时访问http://localhost:5000, 便可以访问flask的程序。
如若想对程序的端口自定义,则在最后一行进行如下修改:app.run(‘0.0.0.0‘, 8080)
注意:此种方式只适用于使用python flask_app.py方式,若使用flask run方式则不生效,如有需要请自行查询资料,后续生产环境不会采用任何直接运行的方式,均会使用uwsgi等代理方式管理程序,后续我们会介绍uwsgi相关使用。
此时程序将会启动在本地的8080端口;
?
什么是 REST?
六条设计规范定义了一个 REST 系统的特点:
?
客户端-服务器: 客户端和服务器之间隔离,服务器提供服务,客户端进行消费。
无状态: 从客户端到服务器的每个请求都必须包含理解请求所必需的信息。换句话说, 服务器不会存储客户端上一次请求的信息用来给下一次使用。
可缓存: 服务器必须明示客户端请求能否缓存。
分层系统: 客户端和服务器之间的通信应该以一种标准的方式,就是中间层代替服务器做出响应的时候,客户端不需要做任何变动。
统一的接口: 服务器和客户端的通信方法必须是统一的。
按需编码: 服务器可以提供可执行代码或脚本,为客户端在它们的环境中执行。这个约束是唯一一个是可选的。
使用flask实现restful services
实现web services的第一个入口
#!flask/bin/python
from flask import Flask, jsonify
app = Flask(__name__)
tasks = [
{
‘id‘: 1,
‘title‘: u‘Buy groceries‘,
‘description‘: u‘Milk, Cheese, Pizza, Fruit, Tylenol‘,
‘done‘: False
},
{
‘id‘: 2,
‘title‘: u‘Learn Python‘,
‘description‘: u‘Need to find a good Python tutorial on the web‘,
‘done‘: False
}
]
@app.route(‘/todo/api/v1.0/tasks‘, methods=[‘GET‘])
def get_tasks():
return jsonify({‘tasks‘: tasks})
if __name__ == ‘__main__‘:
app.run(debug=True)
如上tasks列表为我们模拟的数据属性,接口返回必须以json或str格式,否则报错。
这里的@app.route中我们定义了web URL访问的路由,及http请求资源(methods)的方式,这里涉及到一个知识点,http请求方式,下面列出常用的http请求资源方式:
========== =============================================== =============================
HTTP 方法 URL 动作
========== =============================================== ==============================
GET http://[hostname]/todo/api/v1.0/tasks 检索任务列表
GET http://[hostname]/todo/api/v1.0/tasks/[task_id] 检索某个任务
POST http://[hostname]/todo/api/v1.0/tasks 创建新任务
PUT http://[hostname]/todo/api/v1.0/tasks/[task_id] 更新任务
DELETE http://[hostname]/todo/api/v1.0/tasks/[task_id] 删除任务
========== ================================================ =============================
下面我们根据常用的http请求资源方式进行接下来的restful services的编写,在上面一个例子中我们已经实现了第一种GET方法,获取任务列表中的所有数据,但很多时候我们页面上并不需要展示所有数据,只需要展示部分数据,然后根据需要进行多次获取,那就用到第二种GET方法,准确的说应该是GET路由参数。
注意:在浏览器中,所有请求均为GET方法,之所以HTTP方法分以上几种,大部分时候是由前端通过ajax/axios等技术请求获取数据后进行数据渲染到页面中,所以在测试restful services时,建议使用curl,或者借助postman工具做专业化的开发测试。
注意:使用postman调用service时,要在请求头中添加Content-Type: application/json
这里我们借助curl访问我们刚才创建的tasks函数:
$ curl -i http://localhost:5000/todo/api/v1.0/tasks
# Result:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 294
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 04:53:53 GMT
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
]
}
?
可见tasks函数只是将tasks字典返回,并没有什么变化,接下来我们将尝试对数据的其他操作:
获取指定数据、修改数据、删除数据
?
获取单个任务数据:
@app.route(‘/todo/api/v1.0/tasks/<int:id>‘, methods=[‘GET‘])
def get_task():
task = list(filter(lambda t: t[‘id‘] == task_id, tasks))
if len(task) == 0:
# abort(404)
return jsonify({‘error‘: ‘no such data.‘})
return jsonify({‘task‘: task[0]})
?
这里我们用到了filter()高阶函数及lambda实现字典过滤,后续还会用到map()函数;
注意:在python3中需要对filter()、map()函数进行转 <list> 后方可赋值使用,否则报错。
?
访问get_task方法:
# 访问<id>为2的数据
$ curl -i http://localhost:5000/todo/api/v1.0/tasks/2
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 151
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 05:21:50 GMT
{
"task": {
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
}
# 访问不存在的<id>数据
$ curl -i http://localhost:5000/todo/api/v1.0/tasks/3
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 238
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 05:21:52 GMT
{‘error‘: ‘no such data.‘}
?
上述测试中我们进行了2次访问,一次为字典中存在的<id>为2的数据,一个是<id>为3不存在的数据,我们会看到数据返回是友好的自定义异常返回,在原文中使用的abort方法笔者不太建议使用,在实际应用中我们会有多个接口可能会出现访问不到数据返回404的情况,我们并不希望所有的404返回都是如下信息:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
这样并不友好,且并不能帮助我们前端开发又或自测的时候有效的发现问题,所以通过return方法返回我们定义的异常问题。
当然这里我们也把自定义abort异常方法实现,方法如下:
from flask import make_response
@app.errorhandler(404)
def not_found(error):
print(error)
return make_response(jsonify({‘error‘: ‘Not found‘}), 404)
?
说明:
@app.errorhandler(404)为flask中的异常装饰器
def not_found(error) 函数定义中的传参error不可留空,原因很简单,装饰器会去捕获traceback并传入not_found函数,可以自行查看error打印具体信息。
make_response函数为flask服务返回方法。
?
对应的abort返回信息为:
$ curl -i http://localhost:5000/todo/api/v1.0/tasks/3
HTTP/1.0 404 NOT FOUND
Content-Type: application/json
Content-Length: 26
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 05:36:54 GMT
{
"error": "Not found"
}
?
上述介绍获取单个数据的方法,及flask的异常处理,接下来就是POST方法, 一般用于数据的添加、更新,表单提交等,方法如下:
from flask import request #http请求处理模块,获取请求头、参数、提交数据等信息
@app.route(‘/todo/api/v1.0/tasks‘, methods=[‘POST‘])
def create_task():
if not request.json or not ‘title‘ in request.json:
return jsonify({‘error‘: ‘args not matching‘})
task = {
‘id‘: tasks[-1][‘id‘] + 1,
‘title‘: request.json[‘title‘],
‘description‘: request.json.get(‘description‘, ""),
‘done‘: False
}
tasks.append(task)
return jsonify({‘task‘: task}), 201
?
请求该方法:
$ curl -i -H "Content-Type: application/json" -X POST -d ‘{"title":"Read a book"}‘ http://localhost:5000/todo/api/v1.0/tasks
HTTP/1.0 201 Created
Content-Type: application/json
Content-Length: 104
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 05:56:21 GMT
{
"task": {
"description": "",
"done": false,
"id": 3,
"title": "Read a book"
}
}
注意:如果你在 Windows 上并且运行 Cygwin 版本的 curl,上面的命令不会有任何问题。然而,如果你使用原生的 curl,命令会有些不同:
curl -i -H "Content-Type: application/json" -X POST -d "{"""title""":"""Read a book"""}" http://localhost:5000/todo/api/v1.0/tasks
?
再次请求我们刚开始的GET方法请求数据,查看数据变化:
$ curl -i http://localhost:5000/todo/api/v1.0/tasks
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 423
Server: Werkzeug/0.8.3 Python/2.7.3
Date: Mon, 20 May 2013 05:57:44 GMT
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
},
{
"description": "",
"done": false,
"id": 3,
"title": "Read a book"
}
]
}
?
此时可以看到我们定义的tasks原始字典中,多出了<id>为3 的新增数据,说明我们POST数据添加接口正常。
关于剩下的更新、删除方法,就不再一一阐述了,以下直接列出实现方法:
@app.route(‘/todo/api/v1.0/tasks/<int:task_id>‘, methods=[‘PUT‘])
def update_task(task_id):
task = list(filter(lambda t: t[‘id‘] == task_id, tasks))
if len(task) == 0:
abort(404)
if not request.json:
abort(400)
if ‘title‘ in request.json and type(request.json[‘title‘]) != unicode:
abort(400)
if ‘description‘ in request.json and type(request.json[‘description‘]) is not unicode:
abort(400)
if ‘done‘ in request.json and type(request.json[‘done‘]) is not bool:
abort(400)
task[0][‘title‘] = request.json.get(‘title‘, task[0][‘title‘])
task[0][‘description‘] = request.json.get(‘description‘, task[0][‘description‘])
task[0][‘done‘] = request.json.get(‘done‘, task[0][‘done‘])
return jsonify({‘task‘: task[0]})
@app.route(‘/todo/api/v1.0/tasks/<int:task_id>‘, methods=[‘DELETE‘])
def delete_task(task_id):
task = list(filter(lambda t: t[‘id‘] == task_id, tasks))
if len(task) == 0:
abort(404)
tasks.remove(task[0])
return jsonify({‘result‘: True})
?
以上就是flask实现restful web services的方法,当然flask可以实现的远不止于此,这里只是起到抛砖引玉的效果,后续的工作开发中大家会慢慢的发现flask的魅力,强大。
原文中这里紧接着介绍了关于web services的基础认证,笔者思量之后决定不在此处进行阐述,在后续的关于restful services的httpauth中会进行系统的介绍。
?
写在最后的话:
技术这一行,是一个不断进步学习的过程,我们在学习别人的东西的同时一定要多思考,多实践,才能变成自己的知识,以用于后续的变现。