java有一套spring但是略显笨重而且对于个人平时玩玩来说总感觉没必要(如果掌握了的话效率还是不错的) python的web框架有django和flask等
先安装环境1
pip install flask
即可 安装最新版
按照官网教程
最小的应用
1 | from flask import Flask |
- 首先我们导入了
Flask
类。 该类的实例将会成为我们的 WSGI 应用。 - 接着我们创建一个该类的实例。第一个参数是应用模块或者包的名称。如果你使用 一个单一模块(就像本例),那么应当使用
__name__
,因为名称会根据这个 模块是按应用方式使用还是作为一个模块导入而发生变化(可能是 ‘main’ , 也可能是实际导入的名称)。这个参数是必需的,这样 Flask 才能知道在哪里可以 找到模板和静态文件等东西。更多内容详见Flask
文档。 - 然后我们使用
route()
装饰器来告诉 Flask 触发函数的 URL 。 - 函数名称被用于生成相关联的 URL 。函数最后返回需要在用户浏览器中显示的信息。
WSGI的介绍:
全称Python Web Server Gateway Interface,指定了web服务器和Python web应用或web框架之间的标准接口,以提高web应用在一系列web服务器间的移植性。 具体可查看 官方文档
从以上介绍我们可以看出:
- WSGI是一套接口标准协议/规范;
- 通信(作用)区间是Web服务器和Python Web应用程序之间;
- 目的是制定标准,以保证不同Web服务器可以和不同的Python程序之间相互通信
请不要使用 flask.py
作为应用名称,这会与 Flask 本身发生冲突。
flask 命令或者 python 的 -m
开关来运行这个应用。在 运行应用之前,需要在终端里导出 FLASK_APP
环境变量:1
2
3$ export FLASK_APP=hello.py
$ flask run
* Running on http://127.0.0.1:5000/
通过flask run -h [host] -p [port]指定主机和端口1
export FLASK_ENV=development
打开开发环境的功能
- 激活调试器。
- 激活自动重载。
- 打开 Flask 应用的调试模式。
还可以通过导出 FLASK_DEBUG=1
来单独控制调试模式的开关,绝对不能在生产环境 中使用调试器
路由
现代 web 应用都使用有意义的 URL ,这样有助于用户记忆,网页会更得到用户的青睐, 提高回头率1
2
3
4
5
6
7
8
9from flask import Flask
app = Flask(__name__)
def hello_world():
return 'index page'
# print(__name__)
def hello():
return 'hello world'
可以动态变化 URL 的某些部分, 还可以为一个函数指定多个规则1
2
3
4
5
6
7
8
9
10
11
12
13
14
def show_user_profile(username):
# 显示用户名
return 'User {}'.format(username)
def show_post(post_id):
# 显示提交整型的用户"id"的结果,注意"int"是将输入的字符串形式转换为整型数据
return 'Post {}'.format(post_id)
def show_subpath(subpath):
# 显示 /path/ 之后的路径名
return 'Subpath {}'.format(subpath)
转换器的主要类型如下:
类型 | 含义 |
---|---|
string | 默认的数据类型,接受没有任何斜杠“/”的字符串 |
int | 接受整型 |
float | 接受浮点类型 |
path | 和 string 类似,但是接受斜杠“/” |
uuid | 只接受 uuid 字符串 |
唯一的 URL / 重定向行为
以下两条规则的不同之处在于是否使用尾部的斜杠。:1
2
3
4
5
6
7
def projects():
return 'The project page'
def about():
return 'The about page'
projects
的 URL 是中规中矩的,尾部有一个斜杠,看起来就如同一个文件夹。 访问一个没有斜杠结尾的 URL 时 Flask 会自动进行重定向,帮你在尾部加上一个斜杠。
about
的 URL 没有尾部斜杠,因此其行为表现与一个文件类似。如果访问这个 URL 时添加了尾部斜杠就会得到一个 404 错误。这样可以保持 URL 唯一,并帮助 搜索引擎避免重复索引同一页面。
构建URL
使用url_for()函数
url_for()
函数用于构建指定函数的 URL。它把函数名称作为第一个 参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。
为什么不在把 URL 写死在模板中,而要使用反转函数 url_for()
动态构建?
- 反转通常比硬编码 URL 的描述性更好。
- 你可以只在一个地方改变 URL ,而不用到处乱找。
- URL 创建会为你处理特殊字符的转义和 Unicode 数据,比较直观。
- 生产的路径总是绝对路径,可以避免相对路径产生副作用。
- 如果你的应用是放在 URL 根路径之外的地方(如在
/myapplication
中,不在/
中),url_for()
会为你妥善处理。
HTTP方法
Web 应用使用不同的 HTTP 方法处理 URL 。当你使用 Flask 时,应当熟悉 HTTP 方法。 缺省情况下,一个路由只回应 GET
请求。 可以使用 route()
装饰器的 methods
参数来处理不同的 HTTP 方法:1
2
3
4
5
6
7
8from flask import request
def login():
if request.method == 'POST':
return do_the_login()
else:
return show_the_login_form()
如果当前使用了 GET 方法, Flask 会自动添加 HEAD
方法支持,并且同时还会 按照 HTTP RFC 来处理 HEAD
请求。同样, OPTIONS
也会自动实现。
静态文件
动态的 web 应用也需要静态文件,一般是 CSS 和 JavaScript 文件。理想情况下你的 服务器已经配置好了为你的提供静态文件的服务。但是在开发过程中, Flask 也能做好 这项工作。只要在你的包或模块旁边创建一个名为 static
的文件夹就行了。 静态文件位于应用的 /static
中。
使用特定的 'static'
端点就可以生成相应的 URL1
url_for('static', filename='style.css')
这个静态文件在文件系统中的位置应该是 static/style.css
。
渲染模板
在 Python 内部生成 HTML 不好玩,且相当笨拙。因为你必须自己负责 HTML 转义, 以确保应用的安全。因此, Flask 自动为你配置 Jinja2 模板引擎
Flask 将会在 templates
文件夹中寻找模板。因此如果你的应用是个模块,这个文件夹在模块的旁边,如果它是一个包,那么这个文件夹在你的包里面1
2
3
4
5
6
7
8
9
10
11
<title>Hello From Flask</title>
{% if name %}
<!-- 如果 name 不为空则将 name 渲染出来 -->
<h1>Hello {{ name }}!</h1>
{% else %}
<!-- 如果 name 为空则打印 Hello World! -->
<h1>Hello World!</h1>
{% endif %}
在模板中你也可以使用 request、session 和 g 对象,也能使用函数 get_flashed_messages() 。
模板继承是十分有用的。如果想要知道模板继承如何工作的话,请阅读文档模板继承。基本的模板继承使得某些特定元素(如标题、导航和页脚)在每一页成为可能
引入css文件1
2
3
4
5
6
7
8
9
10
11
12
13
<html lang="zh-CN">
<head>
<link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='style.css') }}"
/>
</head>
<body>
<h1>Hello ShiYanLou {{name}}</h1>
</body>
</html>
接收请求数据
在 Flask 中由全局对象 request
来提供这些信息。如果你有一定的 Python 经验,你会好奇这个对象怎么可能是全局的,并且 Flask 是怎么还能保证线程安全。答案是上下文作用域
请求对象
首先你需要从 flask 模块中导入 request
:1
from flask import request
当前请求的方法可以用method
属性来访问。你可以用form
属性来访问表单数据 (数据在 POST
或者PUT
中传输)。
如果在form
属性中不存在上述键值会发生些什么?在这种情况下会触发一个特别的 KeyError
。你可以像捕获标准的KeyError
一样来捕获它,如果你不这样去做,会显示一个HTTP 400 Bad Request
错误页面。所以很多情况下你不需要处理这个问题。
你可以用args
属性来接收在URL ( ?key=value )
中提交的参数:1
searchword = request.args.get('key', '')
我们推荐使用get
来访问 URL 参数或捕获KeyError
,因为用户可能会修改 URL,向他们显示一个400 bad request
页面不是用户友好的。
文件上传
你能够很容易地用 Flask 处理文件上传。只要确保在你的 HTML 表单中不要忘记设置属性 enctype="multipart/form-data"
,否则浏览器将不会传送文件。
上传的文件是存储在内存或者文件系统上一个临时位置。你可以通过请求对象中files
属性访问这些文件。每个上传的文件都会存储在这个属性字典里。它表现得像一个标准的 Python file
对象,但是它同样具有save()
方法,该方法允许你存储文件在服务器的文件系统上。
已上传的文件被储存在内存或文件系统的临时位置。你可以通过请求对象 files
属性来访问上传的文件。每个上传的文件都储存在这个 字典型属性中。这个属性基本和标准 Python file
对象一样,另外多出一个 用于把上传文件保存到服务器的文件系统中的 save()
方法。下例展示其如何运作:1
2
3
4
5
6
7from flask import request
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
如果你想要知道在上传到你的应用之前在客户端的文件名称,你可以访问filename
属性。但请记住永远不要信任这个值,因为这个值可以伪造。如果你想要使用客户端的文件名来在服务器上存储文件,把它传递到 Werkzeug
提供给你的 secure_filename()
函数:1
2
3
4
5
6
7
8
9from flask import request
from werkzeug import secure_filename
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))
...
文件上传方案
1 | import os |
首先我们导入了一堆东西,大多数是浅显易懂的。 werkzeug.secure_filename()
会在稍后解释。 UPLOAD_FOLDER
是上传文 件要储存的目录, ALLOWED_EXTENSIONS
是允许上传的文件扩展名的集合。
下一个函数检查扩展名是否合法,上传文件,把用户重定向到已上传文件的 URL: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
31def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def upload_file():
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',
filename=filename))
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form>
'''
那么 secure_filename()
函数到底是有什么用?有一条原 则是“永远不要信任用户输入”。这条原则同样适用于已上传文件的文件名。所有提 交的表单数据可能是伪造的,文件名也可以是危险的。此时要谨记:在把文件保存到 文件系统之前总是要使用这个函数对文件名进行安检。
cookies
要访问 cookies ,可以使用 cookies
属性。可以使用响应 对象 的 set_cookie
方法来设置 cookies 。请求对象的 cookies
属性是一个包含了客户端传输的所有 cookies 的字典。在 Flask 中,如果使用 会话 ,那么就不要直接使用 cookies ,因为 会话 比较安全一些。
读取 cookies:1
2
3
4
5
6
7from flask import request
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookie is missing.
重定向
使用 redirect()
函数可以重定向。使用 abort()
可以 更早退出请求,并返回错误代码:1
2
3
4
5
6
7
8
9
10from flask import abort, redirect, url_for
def index():
return redirect(url_for('login'))
def login():
abort(401)
this_is_never_executed()
上例实际上是没有意义的,它让一个用户从索引页重定向到一个无法访问的页面(401 表示禁止访问)。但是上例可以说明重定向和出错跳出是如何工作的。
缺省情况下每种出错代码都会对应显示一个黑白的出错页面。使用 errorhandler()
装饰器可以定制出错页面:1
2
3
4
5from flask import render_template
def page_not_found(error):
return render_template('page_not_found.html'), 404
注意 render_template()
后面的 404
,这表示页面对就的出错 代码是 404 ,即页面不存在。缺省情况下 200 表示:一切正常
响应
一个视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串,它被转换成一个响应主体是该字符串,错误代码为 200 OK
,媒体类型为text/html
的响应对象。Flask 把返回值转换成响应对象的逻辑如下:
- 如果返回的是一个合法的响应对象,它会直接从视图返回。
- 如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
- 如果返回的是一个元组而且元组中元素能够提供额外的信息。这样的元组必须是(
response, status, headers
) 形式且至少含有其中的一个元素。status
值将会覆盖状态代码,headers
可以是一个列表或额外的消息头值字典。 - 如果上述条件均不满足,Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象
会话
除了请求对象之外还有一种称为 session
的对象,允许你在不同请求 之间储存信息。这个对象相当于用密钥签名加密的 cookie ,即用户可以查看你的 cookie ,但是如果没有密钥就无法修改它。
使用会话之前你必须设置一个密钥。举例说明: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
30from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
这里用到的 escape()
是用来转义的。如果不使用模板引擎就可以像上例 一样使用这个函数来转义
消息闪烁
好的应用和用户界面全部是关于反馈。如果用户得不到足够的反馈,他们可能会变得讨厌这个应用。Flask 提供了一个真正的简单的方式来通过消息闪现系统给用户反馈。消息闪现系统基本上使得在请求结束时记录信息并在下一个 (且仅在下一个)请求中访问。通常结合模板布局来显示消息。
使用flash()
方法来闪现一个消息,使用get_flashed_messages()
能够获取消息,get_flashed_messages()
也能用于模板中。
日志和整合 WSGI 中间件
日志
有时候你会遇到一种情况:理论上来说你处理的数据应该是正确的,然而实际上并不正确。比如你可能有一些客户端代码,代码向服务器发送一个 HTTP 请求,但是显然它是错误的。这可能是由于用户篡改数据,或客户端代码失败。大部分时候针对这一情况返回400 Bad Request
就可以了,但是有时候不能这样做,代码必须继续工作。
你也有可能想要记录一些发生的不正常事情。这时候日志就派上用处。从 Flask 0.3 开始日志记录是预先配置好的
整合 WSGI 中间件
如果你想给你的应用添加 WSGI 中间件,你可以封装内部 WSGI 应用。例如如果你想使用 Werkzeug 包中的某个中间件来应付 lighttpd 中的 bugs,你可以这样做:1
2from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
实践
包括四个视图函数的编写:主页视图、新建博客视图、登录视图和等初始图以及三个模板文件。最后编写了针对页面进行美化的 style.css 文件。完成后,即可启动项目并执行登录、编写博客等操作