一、Flask 简介
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
Flask也被称为 “microframework” ,因为它使用简单的核心,并且被设计为可扩展形式。故而没有提供一些重要的功能,例如数据库和用户认证,所以开发者可以自由选择最适合程序的包,或者按照需求自行开发。
社区成员开发了大量的不同用途的扩展,如果这还不能满足需求,你还可以使用所有Python标准包或代码库。
image.png二、Flask 安装
一般安装python后会自带pip工具,方便下载安装python各类库,如果没有自行百度。
pip install flask
三、最简单Flask应用
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World!"
if __name__ == '__main__':
app.run() # 启动服务器
写完后直接用python运行,默认监听本地5000端口,访问http://127.0.0.1:5000/就能看到Hello World!
四、初始化应用
所有Flask程序都必须创建一个程序实例
,所有功能都是围绕这个实例进行的,接收自客户端的所有请求都将转交到这个对象处理。程序实例是Flask类的对象,经常使用下述代码创建一个实例:
from flask import Flask
app = Flask(__name__)
五、路由和视图函数
-
路由
客户端(如Web浏览器)把请求发送给Web服务器,Web服务器再把请求发送给Flask程序实例,程序实例需要知道对每个URL请求运行哪些代码,所以保存了一个URL到Python函数的映射关系。处理URL和函数之间关系的程序称为路由
。
-
视图函数
前面最简单的Flask应用中定义了一个函数hello_world()
,该函数被注册为程序根地址(/)的处理程序,也就是访问http://127.0.0.1:5000/时,会触发服务器执行hello_world()
函数,函数的返回值成为响应
,是客户端接收到的内容,如果客户端是Web浏览器,响应就是返回给用户的HTML文档。
像hello_world()
这样的用于处理请求的函数称为视图函数
。视图函数可以返回简单的HTML字符串,也可以返回一个完整的HTML文档(这里就要用到Jinja2模板引擎啦)。
- 注册一个视图函数最常用的方法就是用
app.route()
:
@app.route("/")
def hello_world():
pass
- 限制路由的请求方法
@app.route("/", methods=["GET"])
def hello_world():
pass
这样,要是客户端以POST的方式请求该路由,就会返回“405 Method Not Allowed”
- 向路由函数传参
有的时候我们需要通过url获取有用的信息,比如说知道来访者的姓名,以便在返回的欢迎语中加入他的名字。
@app.route("/hello/<string:name>")
def hello(name):
return "hello, " + name
上例中,在定义该路由的访问路径里加了一个<string:name>,并增加了一个同名函数参数。
更一般的情况是<type:param>,其中type是类型,常用的有string、int,param就是参数名,它会传递到路由函数的同名参数里。
六、启动服务器
当Flask应用被初始化且视图函数定义好之后,就可以启动Flask应用啦。
if __name__ == "__main__":
app.run() # 默认监听本地5000端口
# app.run(port=8080) 监听8080端口
七、请求 - 响应循环
说到请求,必须要先介绍一下上下文
-
上下文(Context)
上下文,在阅读文章时经常提到,意思是语境、语意。在程序设计中,上下文的概念也是类似的,你可以这么理解:
通俗地讲,每一段代码都有许多外部变量,比如一个函数需要传入若干个必填的参数才能够运行,那些参数就是上下文的一部分,意味着该函数无法独立运行,需要上下文
的支持。而许多函数都是需要各种参数才能运行的,这些参数组成的集合就称为上下文。
-
Flask中的上下文
变量名 | 上下文 | 说明 |
---|---|---|
current_app | 程序上下文 | 当前激活程序的程序实例 |
g | 程序上下文 | 上下文全局变量 |
request | 请求上下文 | 请求对象,封装了客户端发出的HTTP请求中的内容 |
session | 请求上下文 | 用户会话,用户储存同一个客户端在多个请求间需要“记住”的信息 |
-
请求上下文
请求上下文变量是在视图函数被触发时传入的一个HTTP请求对象,储存了所有客户端发送过来的数据,因此该变量只有在视图函数中才能访问。如果难以理解,你只需要记住request变量只有在视图函数中才能访问
我们可以通过以下方式引入请求上下文变量:
from flask import request
-
请求上下文变量常用的访问操作有:
request.args 获取所有GET请求参数
request.form 获取所有form-data、x-www-form-urlencoded类型的请求参数
request.json 获取所有json类型的请求参数
request.method 获取请求方法
request.headers 获取请求头 -
请求钩子
-
什么是钩子(Hook)?
打个比方,鱼钩是用来钓鱼的,一旦鱼咬了钩,钩子就一直钩住鱼了,任凭鱼在水里怎么游,也逃不出鱼钩的控制。在程序设计里,钩子就是一个事件,钩住了某段代码,任凭这段代码这么运行,都逃不过我的钩子事件。 -
Flask里的请求钩子
钩子 说明 before_first_request 注册一个函数,在处理第一个请求之前运行 before_request 注册一个函数,在每次请求之前运行 after_request 注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行 teardown_request 注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行 -
用法
from flask import request @app.before_request def app_before_request(): print("HTTP {} {}".format(request.method, request.url))
这样在每次请求时都会输出请求方式和请求url,但其实钩子函数的作用远不止这些。
-
-
响应
通常,每个视图函数都有返回值,而最简单的就是一串字符串了,Flask默认返回的类型是text/html,状态码为200。状态码很重要,如果需要修改,我们可以这样做:
@app.route("/") def hello_word(): return "Not Found", 404
当然,如果要自定义响应,最好还是引入make_response函数,该函数接受一个bytes对象(二进制数据)或者字符串作为响应主体,并返回一个响应对象response,通过这个对象我们可以自定义该响应的头部,比如修改响应头部的Content-Type和Content-Disposition来告诉客户端这个文件是需要被下载的,并且将From设置为Ncuhome来告诉客户端该文件来自家园工作室。
@app.route("/download") def download(): response = make_response(open("lesson.md", "rb").read()) response.headers["From"] = "Ncuhome" response.headers["Content-Type"] = "application/octet-stream; charset=utf-8;" response.headers["Content-Disposition"] = "attachment;filename=lesson.md" return response
-
快速构建一个json格式的响应
前后端交互时,一般都是使用JSON格式数据进行交互
from flask import jsonify @app.route('/hello', methods=['GET']) def user(user_id): if not user_id == 1: abort(404) return jsonify({ "status": 1, "data": { "username": "ncuhome", "desc": "Hello, Ncuhomer!", }, "message": "获取成功" })
-
其他一些响应
- 重定向
from flask import redirect @app.route("/ncuos") def ncuos(): return redirect("https://www.ncuos.com/")
- 重定向到某个路由
from flask import url_for @app.route("/redirect_to_hello") def redirect(): return redirect(url_for("hello")) @app.route("/hello") def hello(): return "Hello, Ncuhomer!"
- 返回HTTP状态码
from flask import abort @app.route("/user/<int:user_id>") def get_user(user_id): abort(404)
Jinja2 模板引擎
前面提到了Jinja2,它的作用其实是为了方便构造HTML文档、以及其他的一些文档内容(邮件等)。
- 响应返回完整HTML
简单举个栗子吧
模板文件 ./template/index.html
其中name就是我们需要填补的,它用花括号{{ }}括起来了。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Ncuhome Demo</title> </head> <body> Hello, {{ name }} </body> </html>
填补name并返回完整HTML:from flask import render_template @app.route("/hello/<string:name>") def hello(name): return render_template("index.html", name=name)
- 模板中的for循环
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Ncuhome Demo</title> </head> <body> {% for user in users %} {{ loop.index }}: {{ user }} <br> {% endfor %} </body> </html>
from flask import render_template @app.route("/users") def hello(name): users = ["Amy", "David", "Sam"] return render_template("index.html", users=users)
- 模板中的if
if和for类似,都拥有一个作用域,用法也一样。{% if name %} {{ name }} {% endif %}
网友评论