【python】flask框架

  • 部分参考来源
  • 什么是RESTful
    • Web API(Web应用程序接口)
    • REST
    • RESTful
  • RESTful api设计规范
  • 什么是Flask
  • 安装Flask
  • 使用Flask
    • 在网页显示"Hello World!"
    • 使用python和Flask实现RESTful services
    • 优化web service接口
    • Flask实现下载功能
  • 结语

部分参考来源

http://www.ruanyifeng.com/blog/2019/09/curl-reference.html
https://www.jianshu.com/p/ee92c9accedd
https://www.cnblogs.com/wang-yaz/p/9237981.html
http://docs.jinkan.org/docs/flask/quickstart.html
https://www.w3cschool.cn/flask/flask_quick_guide.html
https://www.jianshu.com/p/ed1f819a7b58
http://www.pythondoc.com/flask-restful/first.html
https://www.jianshu.com/p/bee9e61aca14
https://www.jianshu.com/p/9bb7e6c683a3

什么是RESTful

Web API(Web应用程序接口)

如果我们想要获取某个电商网站的某个商品,输入http://localhost:9999/products/123,就可以看到id为123的商品页面,但这个结果是HTML页面,它同时混合包含了商品的数据和商品的展示两个部分。对于用户来说,阅读起来没有问题,但是,如果机器读取,就很难从HTML中解析出商品的数据。

如果一个URL返回的不是HTML,而是机器能直接解析的数据,这个URL就可以看成是一个Web API。比如,读取http://localhost:9999/api/products/123,如果能直接返回商品的数据,那么机器就可以直接读取。

REST

REST是一种设计Web API的模式。最常用的数据格式是JSON。

REST,英文Representational state transfer(表述性状态转移),其实就是对资源的表述性状态转移。
(什么是表述性:就是指客户端请求一个资源,服务器拿到的这个资源,就是表述)
资源的地址在web中就是URL(统一资源标识符)
资源是REST系统的核心概念。 所有的设计都是以资源为中心

REST架构的主要原则:

  • 网络上的所有事物都被抽象为资源
  • 每个资源都有一个唯一的资源标识符
  • 同一个资源具有多种表现形式(XML,JSON等)
  • 对资源的各种操作不会改变资源标识符
  • 所有的操作都是无状态的

六条设计规范定义了一个REST系统的特点:

  • 客户端-服务器:客户端和服务器之间隔离,服务器提供服务,客户端进行消费。
  • 无状态:从客户端到服务器的每个请求都必须包含理解请求所必需的信息。换句话说,服务器不会存储客户端上一次请求的信息用来给下一次使用。
  • 可缓存:服务器必须明示客户端请求能否缓存
  • 分层系统:客户端和服务器之间的通信应该以一种标准的方式。
  • 统一的接口:服务器和客户端的通信方法必须是统一的。
  • 按需执行代码:服务器可以提供可执行代码或脚本,为客户端在它们的环境中执行。这个约束是唯一一个是可选的

RESTful

符合REST原则和特点的架构方式即可称为RESTful

RESTful api设计规范

URL的定义,叫做统一资源定位符,也就是说URL是用来表示资源在互联网上的位置的,所以说在URL中不应该包含动词,只能包含名词。对资源的操作应该体现在http method上面

什么是Flask

Flask是一个使用 python 编写的轻量级Web应用框架

安装Flask

安装Flask库

sudo pip3 install Flask

使用Flask

在网页显示"Hello World!"

# -*- coding:utf-8 -*-from flask import Flaskapp = Flask(__name__)@app.route('/')
def hello_world():return 'Hello World!'if __name__ == '__main__':app.run()

运行python脚本后,访问 http://127.0.0.1:5000/,你会看见Hello World问候。

那么,这段代码做了什么?

  • 首先,我们导入了 Flask 类。这个类的实例将会是我们的WSGI应用程序。Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
from flask import Flask
  • 接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称如果你使用单一的模块(如本例),你应该使用 __name__,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同(也即是 '__main__ ’ 或实际的导入名)。这是必须的,这样 Flask 才知道到哪去找模板、静态文件等等。
app = Flask(__name__)
  • 然后,我们使用 route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数。
@app.route('/')
  • 这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
def hello_world():return 'Hello World!'
  • 最后我们用 run() 函数来让应用运行在本地服务器上。 其中 if __name__ == ‘__main__’: 确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。
if  __name__ == '__main__':app.run()
  • 欲关闭服务器,按 Ctrl+C。

使用python和Flask实现RESTful services

在 Flask 中有许多扩展来帮助我们构建RESTful services。

我们web service的客户端需要添加、删除以及修改任务的服务,因此显然我们需要一种方式来存储任务。最直接的方式就是建立一个小型的数据库。

这里我们直接把任务列表存储在内存中,因此这些任务列表只会在web服务器运行中工作,在结束的时候就失效。这种方式只是适用我们自己开发的web服务器,不适用于生产环境的web服务器, 这种情况一个合适的数据库的搭建是必须的。

我们现在来实现web service的第一个入口:

# -*- coding:utf-8 -*-from flask import Flask, jsonifyapp = 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)

正如你所见,没有多大的变化。我们创建一个任务的内存数据库,这里无非就是一个字典和数组。数组中的每一个元素都具有上述定义的任务的属性。
创建一个get_tasks的函数,访问的URI为 /todo/api/v1.0/tasks并且只允许 GET 的 HTTP 方法

这个函数的响应不是文本,我们使用JSON数据格式来响应,Flask的jsonify函数从我们的数据结构中生成。

使用网页浏览器来测试我们的web service不是一个最好的注意,因为网页浏览器上不能轻易地模拟所有的HTTP请求的方法。相反,我们会使用curl:

curl -i http://localhost:5000/todo/api/v1.0/tasks

返回如下:

我们已经成功地调用我们的RESTful service的一个函数!

现在我们开始编写GET方法请求我们的任务资源的第二个版本。这是一个用来返回单独一个任务的函数:

# -*- coding:utf-8 -*-from flask import Flask, jsonify, abortapp = 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/<int:task_id>', methods=['GET'])
def get_task(task_id):task = list(filter(lambda t: t['id'] == task_id, tasks))if len(task) == 0:abort(404)return jsonify({'task': task[0]})if __name__ == '__main__':app.run(debug=True)

第二个函数有些意思。这里我们得到了URL中任务的id,接着 Flask 把它转换成函数中的task_id的参数。

我们用这个参数来搜索我们的任务数组。如果我们的数据库中不存在搜索的id,我们将会返回一个类似404的错误,根据HTTP规范的意思是 “资源未找到”。

如果我们找到相应的任务,那么我们只需将它用jsonify打包成 JSON 格式并将其发送作为响应,就像我们以前那样处理整个任务集合。

调用curl请求的结果如下:

curl -i http://localhost:5000/todo/api/v1.0/tasks/2

curl -i http://localhost:5000/todo/api/v1.0/tasks/3


当我们请求id #2的资源时候,我们获取到了,但是当我们请求#3的时候返回了404错误。有关错误奇怪的是返回的是HTML信息而不是JSON,这是因为Flask按照默认方式生成404响应。由于这是一个Web service客户端希望我们总是以JSON格式回应,所以我们需要改善我们的404错误处理程序:

# -*- coding:utf-8 -*-from flask import Flask, jsonify, abort, make_responseapp = 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.errorhandler(404)
def not_found(error):return make_response(jsonify({'error': 'Not found'}), 404)@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):task = list(filter(lambda t: t['id'] == task_id, tasks))if len(task) == 0:abort(404)return jsonify({'task': task[0]})if __name__ == '__main__':app.run(debug=True)

我们会得到一个友好的错误提示:

接下来就是POST方法,我们用来在我们的任务数据库中插入一个新的任务:

# -*- coding:utf-8 -*-from flask import Flask, jsonify, abort, make_response, requestapp = 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})@app.errorhandler(404)
def not_found(error):return make_response(jsonify({'error': 'Not found'}), 404)@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):task = list(filter(lambda t: t['id'] == task_id, tasks))if len(task) == 0:abort(404)return jsonify({'task': task[0]})@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():if not request.json or not 'title' in request.json:abort(400)task = {'id': tasks[-1]['id'] + 1,'title': request.json['title'],'description': request.json.get('description', ""),'done': False}tasks.append(task)return jsonify({'task': task}), 201if __name__ == '__main__':app.run(debug=True)

添加一个新的任务也是相当容易。只有当请求以JSON格式形式,request.json才会有请求的数据。如果没有数据,或者存在数据但是缺少title项,我们将会返回 400,这是表示请求无效。

接着我们会创建一个新的任务字典,使用最后一个任务的id + 1作为该任务的 id。我们允许description字段缺失,并且假设done字段设置成False。

我们把新的任务添加到我们的任务数组中,并且把新添加的任务和状态201响应给客户端。

使用如下的 curl 命令来测试这个新的函数:

curl -i -H "Content-Type: application/json" -X POST -d '{"title":"Read a book"}' http://localhost:5000/todo/api/v1.0/tasks

curl -i http://localhost:5000/todo/api/v1.0/tasks/3


当然在完成这个请求后,我们可以得到任务的更新列表:

curl -i http://localhost:5000/todo/api/v1.0/tasks


剩下的两个函数如下所示:

# -*- coding:utf-8 -*-from flask import Flask, jsonify, abort, make_response, requestapp = 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})@app.errorhandler(404)
def not_found(error):return make_response(jsonify({'error': 'Not found'}), 404)@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):task = list(filter(lambda t: t['id'] == task_id, tasks))if len(task) == 0:abort(404)return jsonify({'task': task[0]})@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():if not request.json or not 'title' in request.json:abort(400)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@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})if __name__ == '__main__':app.run(debug=True)

delete_task函数没有什么特别的。对于update_task函数,我们需要严格地检查输入的参数以防止可能的问题。我们需要确保在我们把它更新到数据库之前,任何客户端提供我们的是预期的格式。

更新任务#2的函数调用如下所示:

curl -i -H "Content-Type: application/json" -X PUT -d '{"done":true}' http://localhost:5000/todo/api/v1.0/tasks/2

优化web service接口

目前API的设计的问题就是迫使客户端在任务标识返回后去构造URI。这对于服务器是十分简单的,但是间接地迫使客户端知道这些URI是如何构造的,这将会阻碍我们以后变更这些URI。

不直接返回任务的id,我们直接返回控制这些任务的完整的URI,以便客户端可以随时使用这些 URI。为此,我们可以写一个小的辅助函数生成一个 “公共” 版本任务发送到客户端:

# -*- coding:utf-8 -*-from flask import Flask, jsonify, abort, make_response, request, url_forapp = 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.errorhandler(404)
def not_found(error):return make_response(jsonify({'error': 'Not found'}), 404)@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):task = list(filter(lambda t: t['id'] == task_id, tasks))if len(task) == 0:abort(404)return jsonify({'task': task[0]})@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():if not request.json or not 'title' in request.json:abort(400)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@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})def make_public_task(task):new_task = {}for field in task:if field == 'id':new_task['uri'] = url_for('get_task', task_id=task['id'], _external=True)else:new_task[field] = task[field]return new_task@app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():return jsonify({'tasks': list(map(make_public_task, tasks))})if __name__ == '__main__':app.run(debug=True)# 局域网下其他设备访问设置方法# app.run(host='0.0.0.0', port=5000, debug=True)

我们将会把上述的方式应用到其它所有的函数上以确保客户端一直看到URI而不是id。

curl -i http://localhost:5000/todo/api/v1.0/tasks

Flask实现下载功能

# -*- coding:utf-8 -*-from flask import Flask, send_from_directoryapp = Flask(__name__)@app.route("/download/<string:file>", methods=["GET"])
def download_file(file):return send_from_directory("/home/zhangchen", filename=file, as_attachment=True)if __name__ == '__main__':app.run(host='0.0.0.0', port=5000, debug=True)

结语

如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。

【python】flask框架相关推荐

  1. python Flask框架如何请求及返回数据——flask详细教程

    python Flask框架如何请求及返回数据--flask详细教程 文章目录: 1 Flask介绍 1.1 Flask简单介绍 1.2 Flask相关资料信息 2 Flask快速入门 2.1 Fla ...

  2. python flask框架剖析_python flask框架实现传数据到js的方法分析

    本文实例讲述了python flask框架实现传数据到js的方法.分享给大家供大家参考,具体如下: 首先要清楚后台和前端交互所采用的数据格式. 一般选JSON,因为和js完美贴合. 后台返回的数据进行 ...

  3. Python Flask框架

    Python Flask框架 Flask框架简介 安装过程 Falsk程序的运行过程 基本语法/结构 如有错误,请指正 Flask框架简介 Flask是一个轻量级的可定制框架,使用Python语言编写 ...

  4. Python Flask框架-开发简单博客-认证蓝图

    作者:Eason_LYC 悲观者预言失败,十言九中. 乐观者创造奇迹,一次即可. 一个人的价值,在于他所拥有的.可以不学无术,但不能一无所有! 技术领域:WEB安全.网络攻防 关注WEB安全.网络攻防 ...

  5. 【25】数据可视化:基于 Echarts + Python Flask框架动态实时大屏范例 - 企业宣传

    目录 效果展示 多主题样式 一. 确定需求方案 1.确定产品上线部署的屏幕分辨率 2.部署方式 二.整体架构设计 三.编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码) 1.前端html代码 - ...

  6. #3使用html+css+js制作网页 番外篇 使用python flask 框架 (I)

    #3使用html+css+js制作网页 番外篇 使用python flask 框架(I 第一部) 0. 本系列教程 1. 准备 a.python b. flask c. flask 环境安装 d. f ...

  7. Python+Flask框架搭建可视化网站

    Python+Flask框架搭建可视化网站 一.项目结构 二.app.py from flask import Flask,render_template import sqlite3app = Fl ...

  8. 基于Python Flask框架+jquery Ajax技术实现的增删改查(CRUD)+Ajax的异步文件上传

    运行界面(话不多说先上图) 运行之后的index界面,有登陆.注册功能 登陆界面,输入数据库中用户名.密码不为空且密码是加密的数据,进入main界面 注册界面,用的bootstrop的弹窗,用户名和密 ...

  9. 帮我用python flask框架写一个可以上传英文pdf然后通过调取百度翻译api翻译为中文然后保存为pdf文件的代码...

    下面是一个简单的代码示例,可以帮助您使用 Python Flask 框架实现上传英文 PDF,并通过调用百度翻译 API 将其翻译为中文,然后保存为 PDF 文件: from flask import ...

  10. python flask框架部署方法

    python flask框架部署方法) 链接: [link]https://dormousehole.readthedocs.io/en/latest/deploying/index.html#dep ...

最新文章

  1. python GUI编程( 二 ) (基于PyQt5)
  2. 2018python好找工作吗-2018年IT行业薪资大揭秘:你拖后腿了吗?
  3. TensorFlow实现人脸检测和人脸识别
  4. SSL 证书变革之时已至,这些变化你都清楚吗?
  5. 如何在 GPU 上加速数据科学
  6. 构建简单的微服务架构
  7. OpenCV--实现图像滑动窗口截取子图操作
  8. 从无到有整合SpringMVC-MyBatis项目(2):搭建SpringMVC项目
  9. 服务器400_瓜分400万filecoin的入门条件:现货服务器
  10. 2017.3.11[bzoj2440][中山市选2011]完全平方数
  11. C++ OpenCV技术实战之身份证离线识别
  12. 架构猿怒了,放大招:详解DoDAF
  13. 微信多人共享Excel表格
  14. Windows设置exe或者bat文件开机启动的几种方法汇总
  15. 一个标准的k-means(误差平方和版本)
  16. 成都VS上海,先锋设计机构与未来建筑的“双城battle”
  17. 技术QA:如何找回丢失的证书模板或将独立CA转换成企业CA?
  18. Java poi读取Excel表格中公式的计算值
  19. 戴尔电脑,耳机插入无反应解决办法
  20. 洛谷P4207 [NOI2005]月下柠檬树(计算几何+自适应Simpson法)

热门文章

  1. python编译型语言和解释型语言
  2. 实训三:文件系统命令及vi编辑
  3. [Opencv基础]人脸磨皮
  4. 性能测试结果分析思路
  5. 容器云职业技能大赛 不一样的比赛
  6. Android 6.0 更新包与已安装应用的签名不一致
  7. 网络版AIS接收机R400N
  8. JAVA对象布局之对象头(Object Header)
  9. 对称加密,非对称加密详解
  10. HACKTHEBOX——Help