Flask 简介

什么是Flask?

Flask是一个用Python编写的Web应用程序框架。Flask基于Werkzeug(WSGI工具包)和Jinja2模板引擎。

什么是Web Framework?

Web Application Framework(Web应用程序框架)或简单的Web Framework(Web框架)表示一个库和模块的集合,使Web应用程序开发人员能够编写应用程序,而不必担心协议,线程管理等低级细节。

WSGI

Web Server Gateway Interface(Web服务器网关接口,WSGI)已被用作Python Web应用程序开发的标准。WSGI是Web服务器和Web应用程序之间通用接口的规范。

Werkzeug

它是一个WSGI工具包,它实现了请求,响应对象和实用函数。这使得能够在其上构建web框架。Flask框架使用Werkzeug作为其基础之一。

jinja2

jinja2是Python的一个流行的模板引擎。Web模板系统将模板与特定数据源组合以呈现动态网页。

Flask通常被称为微框架。它旨在保持应用程序的核心简单且可扩展。Flask没有用于数据库处理的内置抽象层,也没有形成验证支持。相反,Flask支持扩展以向应用程序添加此类功能。一些受欢迎的Flask扩展将在本教程后续章节进行讨论。

Flask及其依赖项适用于Python 3,安装Flask之前确认已经安装Python3,本教程默认使用最新版本Python3.7

Flask 环境搭建

方法一:为开发环境安装virtualenv

virtualenv是一个虚拟的Python环境构建器。它可以帮助用户并行创建多个Python环境。因此,它可以避免不同版本的库之间的兼容性问题。

以下命令用于安装virtualenv:

pip install virtualenv

此命令需要管理员权限。您可以在Linux / Mac OS上的 pip 之前添加 sudo 。如果您使用的是Windows,请以管理员身份登录。在Ubuntu上, virtualenv可以使用它的包管理安装。

Sudo apt-get install virtualenv

安装后,将在文件夹中创建新的虚拟环境。

mkdir newproj
cd newproj
virtualenv venv

要在 Linux 上激活相应的环境,请使用以下命令:

venv/bin/activate

要在 Windows 上激活相应的环境,可以使用以下命令:

venv\scripts\activate

我们现在准备在这个环境中安装Flask:

pip install Flask

上述命令可以直接运行,不需要系统范围安装的虚拟环境。

方法二,直接pip安装Flask

打开cmd,键入以下命令:

pip install Flask

等待安装完成即可,本教程建议,可以暂时不采用安装virtualenv虚拟环境,实际生产中再采用方法一,非虚拟环境对Flask的操作是完全一样的。

假设已经安装了Flask,为了测试 Flask 安装,在编辑器中将以下代码输入 Hello.py文件中:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
   return 'Hello World'

if __name__ == '__main__':
   app.run()

那么上面这个代码做了什么?

  1. 首先我们导入了这个Flask类。这个类的一个实例将是我们的WSGI应用程序。
  2. 接下来,我们创建此类的实例。第一个参数是应用程序模块或包的名称。如果您使用的是单个模块(如本示例所示),则应使用__name__,因为根据它是作为应用程序启动还是作为模块导入,名称将不同('__main__'与实际导入名称相对)。这是必需的,以便Flask知道在哪里查找模板,静态文件等。
  3. 然后我们使用route()装饰器告诉Flask应该触发我们的函数的URL。
  4. 该函数被赋予一个名称,该名称也用于为该特定函数生成URL,并返回我们想要在用户的浏览器中显示的消息。
  • 必须在项目中导入Flask模块。Flask类的一个对象是我们的WSGI应用程序。
  • Flask构造函数使用当前模块(__name __)的名称作为参数。
  • Flask类的route()函数是一个装饰器,它告诉应用程序哪个URL应该调用相关的函数。
app.route(rule, options)
  • rule 参数表示与该函数的URL绑定。
  • options 是要转发给基础Rule对象的参数列表。

在上面的示例中,'/ ' URL与hello_world()函数绑定。因此,当在浏览器中打开web服务器的主页时,将呈现该函数的输出。

最后,Flask类的run()方法在本地开发服务器上运行应用程序。

app.run(host, port, debug, options)

所有参数都是可选的

序号参数与描述
1

host

要监听的主机名。 默认为127.0.0.1(localhost)。设置为“0.0.0.0”以使服务器在外部可用

2

port

默认值为5000

3

debug

默认为false。 如果设置为true,则提供调试信息

4

options

要转发到底层的Werkzeug服务器。

这里注意,如果使用python自带的idle运行的时候可能会报以下错误: 

Traceback (most recent call last): 

File “C:/learn/python/xuexi/web/demoflask/app.py”, line 27, in 

app.run(); 

File “C:\Users\zhang\AppData\Local\Programs\Python\Python36\lib\site-packages\flask\app.py”, line 938, in run 

cli.show_server_banner(self.env, self.debug, self.name, False) 

File “C:\Users\zhang\AppData\Local\Programs\Python\Python36\lib\site-packages\flask\cli.py”, line 629, in show_server_banner 

click.echo(message) 

File “C:\Users\zhang\AppData\Local\Programs\Python\Python36\lib\site-packages\click\utils.py”, line 217, in echo 

file = _default_text_stdout() 

File “C:\Users\zhang\AppData\Local\Programs\Python\Python36\lib\site-packages\click_compat.py”, line 621, in func 

rv = wrapper_func() 

File “C:\Users\zhang\AppData\Local\Programs\Python\Python36\lib\site-packages\click_compat.py”, line 385, in get_text_stdout 

rv = _get_windows_console_stream(sys.stdout, encoding, errors) 

File “C:\Users\zhang\AppData\Local\Programs\Python\Python36\lib\site-packages\click_winconsole.py”, line 261, in _get_windows_console_stream 

func = _stream_factories.get(f.fileno()) 

io.UnsupportedOperation: fileno

只要不用idle执行就不会出错了,不影响后续使用。改用cmd下Python执行或者pycharm等运行都能成功。

Windows环境下在cmd下输入以下命令执行:

Python Hello.py

Python shell中的消息通知:

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

在浏览器中打开上述URL(localhost:5000)。将显示“Hello World”消息。

调试模式

通过调用run()方法启动Flask应用程序。但是,当应用程序正在开发中时,应该为代码中的每个更改手动重新启动它。为避免这种不便,请启用调试支持。如果代码更改,服务器将自行重新加载。它还将提供一个有用的调试器来跟踪应用程序中的错误(如果有的话)。

在运行或将调试参数传递给run()方法之前,通过将application对象的debug属性设置为True来启用Debug模式。

app.debug = True
app.run()
或
app.run(debug = True)

Flask 路由

现代Web框架使用路由技术来帮助用户记住应用程序URL。可以直接访问所需的页面,而无需从主页导航。

正如前面所说,route 装饰器是用于把一个函数绑定到一个 URL 上,例如:

from flask import Flask
app = Flask(__name__)

如果访问/,浏览器返回“Index Page”
@app.route('/')
def index():
    return 'Index Page'

如果访问/hello,浏览器返回“Hello,World”
@app.route('/hello')
def hello():
    return 'Hello, World'
if __name__ ="__main__":
    app.run()

访问地址 http://127.0.0.1:5000,浏览器页面会显示 Index Page;如果访问地址 http://127.0.0.1:5000/hello,浏览器页面会显示 Hello, World!。这样就实现了通过访问不同的 URL 地址从而响应不同的页面。

不仅如此!你可以动态地构造 URL 的特定部分(正则表达式),也可以在一个函数上绑定多个不同的规则。

application对象的add_url_rule()函数也可用于将URL与函数绑定,如上例使用route()可以改成如下:

from flask import Flask
app = Flask(__name__)

#如果访问/,浏览器返回“Index Page”
def index():
    return 'Index Page

#如果访问/hello,浏览器返回“Hello,World”
def hello():
    return 'Hello, World'
app.add_url_rule(‘/’, ‘hello’, hello_world)
app.add_url_rule(‘/’, index)

if __name__ ="__main__":
    app.run()

Flask 变量规则 

通过向规则参数添加变量部分,可以动态构建URL。此变量部分标记为<变量名称>。它作为关键字参数传递给与规则相关联的函数。在以下示例中,route()装饰器的规则参数包含附加到URL'/ hello'的<name>。因此,如果在浏览器中输入http:// localhost:5000 / hello / w3cschool作为URL,则' w3cschool“将作为参数提供给你好()函数。

@app.route('/user/<username>')
def show_user_profile(username):
    #显示用户名
    return 'User {}'.format(username)

@app.route('/post/<int:post_id>')
def show_post(post_id):
    #显示提交整型的用户"id"的结果,注意"int"是将输入的字符串形式转换为整型数据
    return 'Post {}'.format(post_id)

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    #显示 /path/ 之后的路径名
    return 'Subpath {}'.format(subpath)

按照前面的方式启动应用,逐个访问地址:

  • 当访问  http://127.0.0.1:5000/user/w3cschool 时,页面显示为用户User w3cschool。
  • 当访问http://127.0.0.1:5000/post/3时,页面显示为帖子3.用户在浏览器地址栏上输入的都是字符串,但是在传递给show_post函数处理时已经被转换为了整型。
  • 当访问http://127.0.0.1:5000/path/file/A/w3cschool .txt时,页面显示为子路径文件/ A / w3cschool .txt。

除了默认字符串变量部分之外,还可以使用以下转换器构建规则:

类型含义
string默认的数据类型,接受没有任何斜杠“/”的字符串
int接受整型
float接受浮点类型
path和 string 类似,但是接受斜杠“/”
uuid只接受 uuid 字符串

唯一的URL /重定向行为

Flask的URL规则基于Werkzeug的路由模块。这确保形成的URL是唯一的,并且基于Apache规定的先例。

考虑以下脚本中定义的规则:

from flask import Flask
app = Flask(__name__)

@app.route('/python')
def hello_flask():
   return 'Hello Flask'
或者
@app.route('/python/')
def hello_python():
   return 'Hello Python'

if __name__ == '__main__':
   app.run()

这两个规则看起来类似,但在第二个规则中,使用斜杠(/)。因此,它成为一个规范的URL。因此,在第二中规则中在浏览器输入/python 或 /python/访问返回相同的输出。但是,如果是第一个规则,输入/python/ 会产生“404 Not Found”页面。

/python/端点的规范URL 具有尾部斜杠。它类似于文件系统中的文件夹。如果您访问的URL没有尾部斜杠,Flask会将您重定向到带有斜杠的规范URL。

/python端点的规范URL 没有尾部斜杠。它类似于文件的路径名。使用尾部斜杠访问URL会产生404“未找到”错误。优点是这有助于保持URL对这些资源的唯一性,这有助于搜索引擎避免两次索引同一页面,提高性能。

Flask URL构建

url_for()函数对于动态构建特定函数的URL非常有用。该函数接受函数的名称作为第一个参数,以及任意数量的关键字参数(每个参数对应于URL的变量部分)。

为什么要使用URL反转功能url_for()而不是将它们硬编码到模板中来构建URL这里有几个理由?

  • 反向通常比对URL进行硬编码更具描述性。
  • 它允许一次性修改URL,而不是到处找URL修改。
  • 网址构建处理特殊字符和统一数据的转义。
  • 生成的路径始终是绝对的,从而避免了浏览器中相对路径的意外行为。
  • 如果您的应用程序位于URL根目录之外,例如,位于/ myapplication而不是/,url_for()正确地为您处理

例如,下面我们使用test_request_context()方法进行尝试url_for()。 test_request_context()告诉Flask,即使我们使用Python shell,它也会像处理请求一样行事。


from flask import Flask, url_for

app = Flask(__name__)

@app.route('/')
def index():
    return 'index'

@app.route('/login')
def login():
    return 'login'

@app.route('/user/<username>')
def profile(username):
    return '{}\'s profile'.format(username)

with app.test_request_context():
    print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next='/'))
    print(url_for('profile', username='John Doe'))


#输出结果如下(使用url_for()构建url):
/
/login
/login?next=/
/user/John%20Doe

以下脚本演示了如何使用url_for()函数:


from flask import Flask, redirect, url_for
app = Flask(__name__)

@app.route('/admin')
def hello_admin():
   return 'Hello Admin'

@app.route('/guest/<guest>')
def hello_guest(guest):
   return 'Hello {} as Guest'.format(guest)

@app.route('/user/<name>')
def hello_user(name):
   if name =='admin':
      return redirect(url_for('hello_admin'))
   else:
      return redirect(url_for('hello_guest',guest = name))

if __name__ == '__main__':
   app.run(debug = True)

上述脚本有一个函数hello_user(名称),它接收来自URL的参数的值。

hello_user(名称)函数检查接收的参数是否与'管理员'匹配。如果匹配,则使用url_for()应用将程序重定向(重定向)到hello_admin()函数,否则将接收的参数作为参数并重定向到传递给它的hello_guest()函数。

保存脚本运行后打开浏览器并输入URL - http:// localhost:5000 / hello / admin

浏览器中的应用程序响应是:

Hello Admin

在浏览器中输入以下URL - http:// localhost:5000 / hello / rrh

应用程序响应现在更改为:

Hello rrh as Guest

Flask HTTP方法

Web应用程序在访问URL时使用不同的HTTP方法。在使用Flask时,您应该熟悉HTTP方法。默认情况下,路由仅GET响应请求。但是能够通过给 route() 装饰器提供 methods 参数来改变,处理不同的HTTP方法。

Http协议是万维网中数据通信的基础。在该协议中定义了从指定URL检索数据的不同方法,HTTP方法(通常也称为“谓词”)告诉服务器客户端想要对请求的页面做什么。。

下表总结了不同的http方法:

序号方法与描述
1

GET

浏览器通知服务器只获取页面上的信息并且发送回来

2

HEAD

浏览器告诉服务器获取信息,但是只对头信息感兴趣,不需要整个页面的内容。应用应该处理起来像接收到一个 GET 请求但是不传递实际内容。在 Flask 中你完全不需要处理它,底层的 Werkzeug 库会为你处理的。

3

POST

浏览器通知服务器它要在 URL 上提交一些信息,服务器必须保证数据被存储且只存储一次。这是 HTML 表单通常发送数据到服务器的方法。POST方法接收的数据不由服务器缓存。

4

PUT

用上传的内容替换目标资源的所有当前表示,而且服务器可能触发了多次存储过程,多次覆盖掉旧值。。考虑在传输过程中连接丢失的情况:浏览器和服务器之间的系统可能安全地第二次接收请求,而不破坏其它东西。该过程操作 POST 方法是不可能实现的,因为它只会被触发一次。

5

DELETE

移除给定位置的信息。

 6 OPTIONS
 
给客户端提供一个快速的途径来指出这个 URL 支持哪些 HTTP 方法。从 Flask 0.6 开始,自动实现了该功能。

举一个栗子:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()        #如果是POST方法就执行登录操作
    else:
        show_the_login_form()   #如果是GET方法就展示登录表单

如果存在GET,Flask会自动添加对HEAD方法的支持,并根据HTTP RFC处理HEAD请求。 同样,OPTIONS会自动为您实施。

为了演示在URL路由中使用POST方法,首先让我们创建一个HTML表单,并使用POST方法将表单数据发送到URL。

将以下脚本另存为login.html

<!DOCTYPE html>


<html>
   <body>
      <form action = "http://localhost:5000/login" method = "post">
         <p>Enter Name:</p>
         <p><input type = "text" name = "nm" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form> 
   </body>
</html>

然后在Python shell中输入以下脚本:

from flask import Flask, redirect, url_for, request
app = Flask(__name__)

@app.route('/success/<name>')
def success(name):
   return 'welcome {}'.format(name)

@app.route('/login',methods = ['POST', 'GET'])
def login():
   if request.method == 'POST':
      user = request.form['nm']
      return redirect(url_for('success',name = user))
   else:
      user = request.args.get('nm')
      return redirect(url_for('success',name = user))

if __name__ == '__main__':
   app.run(debug = True)

服务器开始运行后,在浏览器中打开login.html,在文本字段中输入name,然后单击提交。

表单数据将POST(提交)到表单标签的action子句中的URL。http://localhost/login映射到login()函数。由于服务器通过POST方法接收数据,因此通过以下步骤获得从表单数据获得的“nm”参数(form中的键)的值:

user = request.form['nm']

它作为变量部分传递给'/ success' 这个URL。浏览器在窗口中显示welcome消息。

在login.html中将方法参数更改为'GET',然后在浏览器中再次打开它。服务器上接收的数据是通过GET方法获得的。通过以下的步骤获得'nm'参数的值:

user = request.args.get(‘nm’)

这里,args是包含表单参数对及其对应值对的列表的字典对象。与'nm'参数对应的值将像之前一样传递到“/ success” 这个URL。

Flask 模板

我们本来是可以以HTML的形式返回绑定到某个URL的函数的输出。例如,在以下脚本中,hello()函数将使用<h1>标签呈现'Hello World'。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
   return '<html><body><h1>'Hello World'</h1></body></html>'

if __name__ == '__main__':
   app.run(debug = True)

但是在 Python脚本中嵌入HTML生成 HTML 并不好玩而且相当繁琐的,因为尤其是在需要放置变量数据和Python语言元素(如条件或循环)时,你必须自行做好 HTML 转义以保持应用程序的安全。由于这个原因,Flask 自动为你配置好 Jinja2 模板,你只需要使用方法 render_template() 来渲染模板。

所有需要做的就只是提供模板的名称以及你想要作为关键字参数传入模板的变量。这是可以利用Flask所基于的Jinja2模板引擎的地方。而不是从函数返回硬编码HTML。

上述脚本修改如下:

from flask import Flask,render_template
app = Flask(__name__)

@app.route('/')
def index():
   return render_template(‘index.html’)

if __name__ == '__main__':
   app.run(debug = True)

Flask 将会在 templates 文件夹中寻找模板,在static文件夹中寻找静态文件。因此如果你的应用是个模块,这个文件夹在模块的旁边,如果它是一个包,那么这个文件夹在你的包里面,比如,应用是模块:

/test.py
/templates
    /index.html
/static
    /hello.js

比如,应用是包:

/test
    /__init__.py
    /templates
        /index.html
    /static
        /hello.js

"web templating system(web模板系统)"指的是设计一个HTML脚本,其中可以动态插入变量数据。web模板系统包括模板引擎,某种数据源和模板处理器。

Flask使用jinga2模板引擎。Web模板包含用于变量和表达式(在这些情况下为Python表达式)的HTML语法散布占位符,这些是在呈现模板时替换的值。

Jinja2模板引擎使用以下分隔符从HTML转义:

  • {% ... %}用于语句
  • {{ ... }}用于表达式可以打印到模板输出
  • {# ... #}用于未包含在模板输出中的注释
  • # ... ##用于行语句

以下代码在templates文件夹中存为index.html 。

<!doctype html>
<html>
   <body>
      <h1>Hello {{ name }}!</h1>  
   </body>
</html>

接下来,从Python shell运行以下脚本:

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/hello/<user>')
def hello_name(user):
   return render_template('index.html', name = user)

if __name__ == '__main__':
   app.run(debug = True)

当开发服务器开始运行时,打开浏览器并输入URL - http://localhost:5000/hello/mvl

URL的变量部分插入{{name}}占位符。

再看一个栗子:

在下面的示例中,演示了在模板中使用条件语句。hello()函数的URL规则接受整数参数。它被传递到hello.html模板。其中,比较接收的数字(marks)的值(大于或小于50),因此有条件地呈现HTML。

Python脚本如下:

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/hello/<int:score>')
def hello_name(score):
   return render_template('hello.html', marks = score)

if __name__ == '__main__':
   app.run(debug = True)

hello.html的HTML模板脚本如下:

<!DOCTYPE html>
<html>
   <body>  
      {% if marks>50 %}
      <h1> Your result is pass!</h1>
      {% else %}
      <h1>Your result is fail</h1>
      {% endif %}      
   </body>
</html>

请注意,条件语句if-else和endif包含在分隔符{%..%}中。

运行Python脚本并访问URL http://localhost/hello/60,然后访问http://localhost/hello/30,以查看HTML的输出是否有条件地更改。

Python循环结构也可以在模板中使用。

再再看一个栗子:

在以下脚本中,当在浏览器中打开URL http://localhost:5000/result时,result()函数会将字典对象发送到模板results.html。

result.html的Template部分使用for循环将字典对象result{}的键和值对呈现为HTML表的单元格。

从Python shell运行以下代码:

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/result')
def result():
   dict = {'phy':50,'che':60,'maths':70}
   return render_template('result.html', result = dict)

if __name__ == '__main__':
   app.run(debug = True)

将以下HTML脚本保存为templates文件夹中的result.html:

<!DOCTYPE html>
<html>
   <body>
   
      <table border = 1>
         {% for key, value in result.iteritems() %}
         
            <tr>
               <th> {{ key }} </th>
               <td> {{ value }} </td>
            </tr>
            
         {% endfor %}
      </table>
      
   </body>
</html>

这里,对应于For循环的Python语句再次包含在{%..%}中,而表达式key和value放在{{}}中。

开始运行后,在浏览器中打开http://localhost:5000/result即可获得以下输出:

在模板中你也可以使用request,session和g对象,也能使用函数get_flashed_messages() 。

模板继承是十分有用的。如果想要知道模板继承如何工作的话,请阅读文档模板继承。基本的模板继承使得某些特定元素(如标题、导航和页脚)在每一页成为可能。

自动转义默认是开启的,因此如name包含 HTML,它将会自动转义。如果你信任一个变量,并且你知道它是安全的(例如一个模块把 wiki 标记转换到 HTML ),你可以用Markup类或|safe过滤器在模板中标记它是安全的。 在 Jinja 2 文档中,你会见到更多例子。

下面有一个Markup类如何工作的基本介绍,在 Python3 交互式命令行中执行如下命令:

$ python3
>>> from flask import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup('<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup('&lt;blink&gt;hacker&lt;/blink&gt;')
>>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
'Marked up » HTML'
>>>

需要注意的是,在后面的0.5版本以上:自动转义不再在所有模板中启用。模板中下列后缀的文件会触发自动转义:.html, .htm, .xml,.xhtml。从字符串加载的模板会禁用自动转义。 

Flask静态文件

动态的 web 应用同样需要静态文件,例如javascript文件或支持网页显示的CSS文件。通常, web 服务器已经配置好为它们服务,然而在开发过程中 ,你需要从您的包或模块旁边的static文件夹中提供。只要在你的包中或模块旁边创建一个名为static 的文件夹,在应用中使用 /static 即可访问。

Flask 就能够做到给静态文件生成 URL ,使用特殊的 static 端点名:

url_for('static', filename='style.css')

这个文件是必须存储在文件系统上的static/style.css。

Flask 将会在 templates 文件夹中寻找模板,在static文件夹中寻找静态文件。因此如果你的应用是个模块,这个文件夹在模块的旁边,如果它是一个包,那么这个文件夹在你的包里面,比如,应用是模块:

/test.py
/templates
    /index.html
/static
    /hello.js

比如,应用是包:

/test
    /__init__.py
    /templates
        /index.html
    /static
        /hello.js

在下面的示例中,在index.html中的HTML按钮的OnClick事件上调用hello.js中定义的javascript函数,该函数在Flask应用程序的“/”URL上呈现。

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def index():
   return render_template("index.html")

if __name__ == '__main__':
   app.run(debug = True)

index.html(放在模板文件夹templates下)的HTML脚本如下所示:

<!DOCTYPE html>
<html>
   <head>
      <script src = "{{ url_for('static', filename = 'hello.js') }}" ></script>
   </head>
   
   <body>
      <input type = "button" onclick = "sayHello()" value = "Say Hello" />
   </body>  
</html>

Hello.js(放在文件夹staic下)包含sayHello()函数。

function sayHello() {
   alert("Hello World")
}

Flask 请求对象

来自客户端网页的数据作为全局请求对象发送到服务器。为了处理请求数据,应该从Flask模块导入Request。

Request对象的重要属性如下所列:

  • Form - 它是一个字典对象,包含表单参数及其值的键和值对。
  • args - 解析查询字符串的内容,它是问号(?)之后的URL的一部分。
  • Cookies  - 保存Cookie名称和值的字典对象。
  • files - 与上传文件有关的数据。
  • method - 当前请求方法。

 首先你需要从 flask 模块中导入request:

from flask import request

当前请求的方法可以用method属性来访问。你可以用form属性来访问获取表单数据 (数据在 POST 或者PUT中传输)。

举一个用到上面提及的两种属性(method和form)的完整的例子:

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # 当请求形式为“GET”或者认证失败则执行以下代码
    return render_template('login.html', error=error)

如果在form属性中不存在上述键值会发生些什么?在这种情况下会触发一个特别的 KeyError。

你可以像捕获标准的KeyError一样来捕获它,如果你不这样去做,会显示一个HTTP 400 Bad Request错误页面。

但是,很多情况下你不需要处理这个问题。你可以用args属性来接收在URL ( ?key=value )中提交的参数:

search_word = request.args.get('key', '')

我们推荐使用get来访问获取 URL 参数或捕获KeyError,因为用户可能会修改 URL,而向他们显示一个400 bad request的页面显然不是用户友好的。

文件上传

用 Flask 处理文件上传非常简单。需要在HTML表单form中,确保设置其enctype属性为“multipart / form-data”,就可以将文件发布到URL,URL处理程序从request.files[]对象中提取文件,并将其保存到所需的位置。

上传的文件首先是存储在内存或者文件系统(服务器)上一个临时位置,然后将其实际保存到它的最终位置。你可以通过请求对象(request)中files属性访问这些文件。

如果你想要知道在上传到你的应用之前在客户端的文件名称,你可以访问filename属性但请记住永远不要信任这个值,因为这个值可以伪造。所以,建议你如果想要使用客户端的文件名来在服务器上存储文件,把它(filename属性)传递到Werkzeug提供给你的secure_filename()函数。

每个上传的文件都会存储在这个属性字典里。它表现得像一个标准的 Python file对象,但是它同样具有save()方法,该方法允许你存储文件在服务器的文件系统上。

就像下面这样:

from flask import request
from werkzeug import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))
    ...

允许在Flask对象的配置设置中定义默认上传文件夹的路径和上传文件的最大大小

app.config[‘UPLOAD_FOLDER’]定义上传文件夹的路径
app.config[‘MAX_CONTENT_PATH’]指定要上传的文件的最大大小(以字节为单位)

举个完整的文件上传的例子:

以下代码具有'/ upload' URL规则,该规则在templates文件夹中显示'upload.html',以及'/ upload-file' URL规则,用于调用uploader()函数处理上传过程。

from flask import Flask, render_template, request
from werkzeug import secure_filename
app = Flask(__name__)

@app.route('/upload')
def upload_file():
     return render_template('upload.html')
	
@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
         f = request.files['file']
         f.save(secure_filename(f.filename))
         return 'file uploaded successfully'
		
if __name__ == '__main__':
    app.run(debug = True)

'upload.html'有一个文件选择器按钮和一个提交按钮:

<!DOCTYPE html>
<html>
   <body>
      <form action = "http://localhost:5000/uploader" method = "POST" 
         enctype = "multipart/form-data">
         <input type = "file" name = "file" />
         <input type = "submit"/>
      </form>      
   </body>
</html>

您将看到如下所示的界面:

选择文件后,单击提交。表单的post方法调用'/ upload_file' URL。底层函数uploader()执行保存操作。

Flask 将表单数据发送到模板

可以在URL规则中指定http方法。触发函数接收的Form数据可以以字典对象的形式收集它并将其转发到模板以在相应的网页上呈现它。

在以下示例中,'/' URL会呈现具有表单的网页(student.html)。填入的数据会发布到触发 result()函数的'/result' URL。

results()函数收集字典对象中的request.form中存在的表单数据,并将其发送给result.html。

该模板动态呈现表单数据的HTML表格。

应用模块为:

/student.py
/templates
    /student.html
    /result.html

student.py的代码如下:

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/')
def student():
   return render_template('student.html')

@app.route('/result',methods = ['POST', 'GET'])
def result():
   if request.method == 'POST':
      result = request.form
      return render_template("result.html",result = result)

if __name__ == '__main__':
   app.run(debug = True)

模板student.html的代码如下:

<!DOCTYPE html>
<html>
   <body>
   
      <form action = "http://localhost:5000/result" method = "POST">
         <p>Name <input type = "text" name = "Name" /></p>
         <p>Physics <input type = "text" name = "Physics" /></p>
         <p>Chemistry <input type = "text" name = "chemistry" /></p>
         <p>Maths <input type ="text" name = "Mathematics" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
      
   </body>
</html>

模板restult.html的代码如下:

<!DOCTYPE html>
<html>
   <body>
   
      <table border = 1>
         {% for key, value in result.items() %}
         
            <tr>
               <th> {{ key }} </th>
               <td> {{ value }} </td>
            </tr>
            
         {% endfor %}
      </table>
      
   </body>
</html>

当点击提交按钮时,表单数据以HTML表格的形式呈现在result.html上:

Flask Cookies

Cookie以文本文件的形式存储在客户端的计算机上。其目的是记住和跟踪与客户使用相关的数据,以获得更好的访问者体验和网站统计信息。

Request对象(请求对象)包含Cookie的属性。它是所有cookie变量及其对应值的字典对象。除此之外,cookie还存储其网站的到期时间,路径和域名。

在Flask中,对响应对象设置cookie的方法:

使用make_response()函数从视图函数的返回值获取响应对象。之后,使用响应对象的set_cookie()方法来存储cookie。

读取cookie的方法:

request.cookies属性的get()方法用于读取cookie。

在以下Flask应用程序中,当您访问'/' URL时,会打开一个简单的表单。

from flask import Flask,render_template,request,make_response

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/setcookie', methods = ['POST', 'GET'])
def setcookie():
    if request.method == 'POST':
        user = request.form['nm']
        resp = make_response(render_template('readcookie.html'))     
        resp.set_cookie('userID', user)
        return resp

@app.route('/getcookie')
def getcookie():
    name = request.cookies.get('userID')    
    return '<h1>welcome '+name+'</h1>'

if __name__ == "__main__":
	app.run(debug=True)

index.html页面包含一个文本输入:

<!DOCTYPE html>
<html>
   <body>  
      <form action = "/setcookie" method = "POST">
         <p><h3>Enter userID</h3></p>
         <p><input type = 'text' name = 'nm'/></p>
         <p><input type = 'submit' value = 'Login'/></p>
      </form>      
   </body>
</html>

表单发布到'/ setcookie' URL。相关联的视图函数设置Cookie名称userID并呈现另一个页面。

'readcookie.html'包含指向另一个视图函数getcookie()的超链接,它读回并在浏览器中显示Cookie值。

readcookies.html 如下:

 

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

   <h1>Cookies 'UseID' is set</h1>

   <a href="/getcookie">Click here to readcookie</a>

</body>

</html>

运行应用程序,并访问http://localhost:5000/

设置cookie的结果显示为这样:

读取cookie的输出如下所示:

注意cookies是在响应对象中被设置。由于通常只是从视图函数返回字符串,Flask 会将其转换为响应对象。如果你要显式地这么做,可以使用 make_response() 函数接着修改它。

有时候可能需要在响应对象不存在的地方设置cookie。利用延迟请求回调模式使得这种情况成为可能。

Flask Sessions(会话)

cookie和session的区别:

1,cookie的数据存放在客户的浏览器上,会话数据放在服务器上。

2,cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用会话。

3,session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。

如图4所示,单个的cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个的​​cookie。

5,所以个人建议:   

  • 将登陆信息等重要信息存放为SESSION   
  • 其他信息如果需要保留,可以放在COOKIE中

会话是客户端登录到服务器并注销服务器的时间间隔。需要在该会话中保存的数据会存储在服务器上的临时目录中。会为每个客户端的会话分配会话ID。

会话(session)是在cookies的基础上实现的,并且在cookies中使用加密的签名。这意味着用户可以查看cookie的内容,但是不能修改它,除非知道签名的密钥。要使用会话,你需要设置一个密钥。会话数据存储在饼干的顶部,服务器以加密方式对其进行签名。对于此加密,瓶应用程序需要一个定义的SECRET_KEY(密钥)。

怎样产生一个好的密钥:

。随机的问题在于很难判断什么是真随机一个密钥应该足够随机你的操作系统可以基于一个密码随机生成器来生成漂亮的随机值,这个值可以用来做密钥:

$ python3 -c 'import os; print(os.urandom(16))'
b'm \xf8>]?\x86\xcf/y\x0e\xc5\xc7j\xc5/'

把上述这个值复制粘贴到你的代码,你就搞定了密钥。

会话对象也是一个字典对象,包含会话变量和关联值的键值对。

例如,要设置一个 '用户名' 会话变量,设置会话的语法:

Session[key] = value

要释放会话变量,请使用弹出()方法,传入键,设置值为无来释放:

session.pop(key, None)

举个栗子介绍会话如何工作,新建  test.py 文件并写入如下代码:

 

from flask import Flask, session, redirect, url_for, request,render_template

app = Flask(__name__)

# 设置密钥,保证会话安全

app.secret_key = '_5#y2L"F4Q8z\n\xec]/'

@app.route('/')

def index():

    if 'username' in session:

        return 'Logged in as {} <br><b><a href = '/logout'>click here to log out</a></b>'.format(session['username'])

    return "You are not logged in <br><a href = '/login'></b>click here to log in</b></a>"

@app.route('/login', methods=['GET', 'POST'])

def login():

    if request.method == 'POST':

        session['username'] = request.form['username']

        return redirect(url_for('index'))

    return render_template('login.html')

@app.route('/logout')

def logout():

    # 如果用户名存在,则从会话中移除该用户名

    session.pop('username', None)

    return redirect(url_for('index'))

if __name__ == "__main__":

    app.run(debug = True)

flask中的 escape()函数可以在你不使用模板引擎的时候做转义。其中,login函数中报道查看的源|网页代码可以单独存储在templates文件夹中作为模板文件html,然后使用return render_template()更方便。

login.html 文件如下:

<!DOCTYPE html>
<html>
   <body>
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>  
   </body>
</html>

当访问首页 http://127.0.0.1:5000/ 时会显示 You are not logged in,和一个访问登录页面 http://127.0.0.1:5000/login的超链接;点击时会出现一个输入框,在输入框中输入用户名,然后点击 Login 按钮,这时 URL 会重定向到首页上,首页显示 Logged in as 用户名;最后再访问登出页面 http://127.0.0.1:5000/logout,这时从 session 中移除了用户名,URL 重定向到首页显示 You are not logged in和一个登录的超链接:

—————————————————————————————————————

—————————————————————————————————————

Flask 重定向和错误

redirect()函数重定向用户到其它地方。能够用abort()函数提前中断一个请求并带有一个错误代码。

重定向

Flask类有一个redirect()函数。调用时,它返回一个响应对象,并将用户重定向到具有指定状态代码的另一个目标位置。

redirect()函数的原型如下:

Flask.redirect(location, statuscode, response)

在上述函数中:

  • location参数是应该重定向响应的URL。
  • statuscode发送到浏览器标头,默认为302。
  • response参数用于实例化响应。

以下状态代码已标准化:

  • HTTP_300_MULTIPLE_CHOICES
  • HTTP_301_MOVED_PERMANENTLY
  • HTTP_302_FOUND
  • HTTP_303_SEE_OTHER
  • HTTP_304_NOT_MODIFIED
  • HTTP_305_USE_PROXY
  • HTTP_306_RESERVED
  • HTTP_307_TEMPORARY_REDIRECT

默认状态代码为302,表示'found'。

在以下示例中,redirect()函数用于在登录尝试失败时再次显示登录页面。

from flask import Flask, redirect, url_for, render_template, request
# Initialize the Flask application
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('log_in.html')

@app.route('/login',methods = ['POST', 'GET'])
def login():
    if request.method == 'POST' and
        request.form['username'] == 'admin' :
        return redirect(url_for('success'))
    return redirect(url_for('index'))

@app.route('/success')
def success():
    return 'logged in successfully'
	
if __name__ == '__main__':
    app.run(debug = True)

错误

Flask类具有带有错误代码的abort()函数。

Flask.abort(code)

Code参数采用以下值之一:

  • 400 - 用于错误请求
  • 401 - 用于未身份验证的
  • 403 - Forbidden
  • 404 - 未找到
  • 406 - 表示不接受
  • 415 - 用于不支持的媒体类型
  • 429 - 请求过多

让我们对上述代码中的login()函数稍作更改。如果要显示'Unauthurized'页面,请将其替换为调用abort(401),而不是重新显示登录页面。

from flask import Flask, redirect, url_for, render_template, request, abort
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('log_in.html')

@app.route('/login',methods = ['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['username'] == 'admin' :
            return redirect(url_for('success'))
        else:
            abort(401)
    else:
        return redirect(url_for('index'))

@app.route('/success')
def success():
    return 'logged in successfully'

if __name__ == '__main__':
    app.run(debug = True)

默认情况下,每个错误代码会显示一个黑白错误页面,如果你想定制错误页面,可以使用errorhandler()装饰器:

@app.errorhandler(401)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

可以向 page_not_found.html 文件中添加html代码,让其显示我们想要的内容。

注意到 404 是在render_template()调用之后。告诉 Flask 该页的错误代码应是 404 ,即没有找到。默认的 200 被假定为:一切正常。

Flask 响应

一个视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串,它被转换成一个响应主体是该字符串,错误代码为 200 OK ,媒体类型为text/html的响应对象。Flask 把返回值转换成响应对象的逻辑如下:

  1. 如果返回的是一个合法的响应对象,它会直接从视图返回。
  2. 如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
  3. 如果返回的是一个元组而且元组中元素能够提供额外的信息。这样的元组必须是(response, status, headers) 形式且至少含有其中的一个元素。status值将会覆盖状态代码,headers可以是一个列表或额外的消息头值字典。
  4. 如果上述条件均不满足,Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象。

如果你想要获取在视图中得到的响应对象,你可以用函数make_response()。

想象你有这样一个视图:

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

你只需要用make_response()封装返回表达式,获取结果对象并修改,然后返回它:

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

Flask消息闪烁

一个好的基于GUI的应用程序会向用户提供有关交互的反馈。例如,桌面应用程序使用对话框或消息框,JavaScript使用警报用于类似目的。

好的应用和用户界面全部是关于反馈。如果用户得不到足够的反馈,他们可能会变得讨厌这个应用。Flask 提供了一个真正的简单的方式来通过消息闪现系统给用户反馈。消息闪现系统基本上使得在请求结束时记录信息并在下一个 (且仅在下一个)请求中访问。通常结合模板布局来显示消息。

在Flask Web应用程序中生成这样的信息性消息很容易。Flask框架的闪现系统可以在一个视图中创建消息,并在名为next的视图函数中呈现它。

使用flash()方法来闪现一个消息,使用get_flashed_messages()能够获取消息,get_flashed_messages()也能用于模板中。

Flask模块包含flash()方法。它将消息传递给下一个请求,该请求通常是一个模板。

flash(message, category)

其中,

  • message参数是要闪现的实际消息。
  • category参数是可选的。它可以是“error”,“info”或“warning”。

为了从会话中删除消息,模板调用get_flashed_messages()。

get_flashed_messages(with_categories, category_filter)

两个参数都是可选的。如果接收到的消息具有类别,则第一个参数是元组。第二个参数仅用于显示特定消息。

让我们看一个简单的例子,演示Flask中的闪现机制。

在以下代码中,'/' URL显示登录页面的链接,没有消息闪现。该链接会将用户引导到'/ login' URL,该URL显示登录表单。提交时,login()视图函数验证用户名和密码,并相应闪现'success'消息或创建'error'变量。如果出现错误,则会重新显示登录模板,并显示错误消息。

下面给出了Flask消息闪现示例的完整代码:

Flash.py

 

from flask import Flask, flash, redirect, render_template, request, url_for

app = Flask(__name__)

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')

def index():

    return render_template('index.html')

@app.route('/login', methods = ['GET', 'POST'])

def login():

    error = None

   

    if request.method == 'POST':

        if request.form['username'] != 'admin' or \

            request.form['password'] != 'secret':

            error = 'Invalid username or password. Please try again!'

        else:

           flash('You were successfully logged in')

           return redirect(url_for('index'))

    return render_template('login.html', error = error)

if __name__ == "__main__":

    app.run(debug = True)

Index.html

<!doctype html>
<html>   
   <head>
      <title>Flask Message flashing</title>
   </head>

   <body>   
      {% with messages = get_flashed_messages() %}
         {% if messages %}
            <ul>
               {% for message in messages %}
               <li<{{ message }}</li>
               {% endfor %}
            </ul>
         {% endif %}
      {% endwith %}
		
      <h1>Flask Message Flashing Example</h1>
      <p>Do you want to <a href = "{{ url_for('login') }}">
         <b>log in?</b></a></p>      
   </body>

</html>

Login.html

<!doctype html>
<html>
   <body>
   
      <h1>Login</h1>
      
      {% if error %}
      <p><strong>Error:</strong> {{ error }}
      {% endif %}
      
      <form action = "" method = post>
         <dl>
            <dt>Username:</dt>
            
            <dd>
               <input type = text name = username 
                  value = "{{request.form.username }}">
            </dd>
            
            <dt>Password:</dt>
            <dd><input type = password name = password></dd>
         </dl>
         <p><input type = submit value = Login></p>
      </form>
      
   </body>
</html>

当访问首页 http://127.0.0.1:5000,会提示 Do you want to log in?,点击链接跳转到登录页面。

在登录页面 http://127.0.0.1:5000/login,输入用户名和密码,如果输入错误的信息,点击 Login,就会出现错误提示Error: Invalid username or password. Please try again! 

如果用户名输入 admin、密码输入 secret,点击 Login,就会跳转到首页,同时在首页会显示 flash 消息 You were successfully logged in。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐