[关闭]
@Mr-13 2019-12-21T02:28:15.000000Z 字数 11225 阅读 60

Flask安装使用

python3


前言

  Flask是一个轻量级的基于python的web框架。
  安装Flask之前,关于python、pip的安装这里不多赘述



1. 安装Flask

  如果是在Linux直接编辑运行代码,需要先安装Flask;如果是使用Pycharm等IDE工具,在IDE直接引用安装即可

  打开CMD,执行:

  1. pip install flask

image



2. 第一个Hello Word程序

  话不多说,用Pycharm开始第一个程序,新建一个工程,键入代码如下:

image

  如果是在Linux下,编辑保存后,进入该文件目录下运行程序

  1. #python hello.py

  Pycharm直接运行,提示如下图字样后,就说明服务启动完成;
  打开浏览器,访问 http://127.0.0.1:5000/ 就可以看到运行效果了

image

image


简单解释下这段代码

1. 首先引入Flask包,并创建一个Web应用的实例"app"

  这里给的实例名就是这个python模块名。

  1. from flasf importFlask
  2. app = Flask(__name__)



2. 定义路由规则

  这个函数级别的注解指明了当前地址是根路径时,就调用下面的函数。可以定义多个路由规则;说的高大上些,这里就是MVC中的Contollr。

  1. @app.route('/')

3. 处理请求

  当请求的地址符合路由规则时,就会进入该函数。可以说,这里是MVC的Model层。你可以在里面获取请求的request对象,返回内容就是response。本例中的response就是大标题“HelloWorld”。

  1. def index()
  2. return '<h1>Hello world</h1>'



4. 启动Web服务器

  当程序为文件入口(也就是python命令直接执行本文件)时,就会通过 app.run() 启动Web服务器。如果不是程序入口,那么该文件就是一个模块。Web服务器会默认监听本地5000端口,但不支持远程访问。
  如果想要支持远程,需要在 run() 方法中传入 host=0.0.0.0 ,想改变监听端口的话,传入 port=端口号 ,还可以设置调试模式。
  
  具体例子如下:

  1. if __name__ == '__main__'
  2. app.run(host='0.0.0.0', port=8888, debug=True)

注意:
  Flask自带的Web服务器主要还是给开发人员调试使用的,在生产环境中,最好还是通过WSGI将Flash部署到Apache或Nginx的服务器上。




3. 路由

  从Hello World中,我们了解到URL的路由可以直接写在其要执行的函数上。有人会质疑,这样不是把MOdel和Controller绑在一起了么?
  的确,如果你想灵活的配置Model和Controller,这样是不方便,但是对于轻量级的系统来说,灵活配置意义不大,反而写在一块更利于维护。
  Flask的路由规则都是基于Werkzeug的路由模块的,它还提供了很多很强大的功能。




3.1 带参数的路由

  我们在上面Hello World的基础上,加上下面的函数。并运行程序

  1. @app.route('/hello/<name>')
  2. def hello(name)
  3. return 'Hello %s' % name

iamge

  当你在浏览器的地址栏中输入 http://127.0.0.1:5000/hello/man ,你将在页面上看到“Hello man”的字样。URL路径中 /hello/ 后面的参数被作为 hello() 函数的 name 参数传了进来。




3.2 参数类型转换器

  你还可以在URL参数前添加转换器来转换参数类型,我们再来加个函数:

  1. @app.route(/user/<int:user_id>)
  2. def get_user(user_id):
  3. return 'User ID: %d' % user_id

  
  试下访问 http://127.0.0.1:5000/user/man ,你会看到404错误。

image

image

  
  但是试下 http://127.0.0.1:5000/user/123 ,页面上就会有“User ID: 123”显示出来。
  
image

  
  参数类型转换器 int: 帮你控制了传入参数类型只能是整形。目前支持的参数类型转换器有:

类型转换器 作用
缺省 字符型,但不能有斜杠
int: 整形
float: 浮点型
path: 字符型,可有斜杠

  另外,大家有没有注意到,Flask自带的Web服务器支持热部署。当你修改好文件保存后,Web服务器自动部署完毕,你无需重新运行程序。

即:
  在Linux下,只要一次执行了*.py文件;后续的修改,只要保存后,立即可以访问新增的内容;Flask会自动部署。





3.3 多URL的路由

  一个函数上可以设置多个URL路由规则:

  1. @app.route('/')
  2. @app.route('/hello/')
  3. @app.route('/hello/<name>')
  4. def hello(name=None):
  5. if name is None:
  6. name = 'World'
  7. return 'Hello %s' % name

  这个例子接受三种URL规则, //hello 都不带参数,函数参数 name 值为空,页面显示“Hello World”;
  /hello/<name> 带参数,页面会显示参数 name 的值,效果与上面的例子相同。




3.4 HTTP请求方法设置

  http请求方法常用的有:get、post、put、delete;Flask路由规则也可以设置请求方法。

  1. from flask import request
  2. @app.route('/login',methods=['GET','POST'])
  3. def login():
  4. if request.methid == 'POST':
  5. return 'This is a POST request'
  6. else:
  7. return 'This is a GET request'

image

image

  当你请求地址 http://127.0.0.1:5000/login ,“GET”和“POST”请求会返回不同的内容,其他请求方法则会返回405错误。




3.5 URL构建方法

  Flask提供了 url_for() 方法来快速获取及构建URL,方法的第一个参数指向函数名(加过 @app.route 注解的函数),后续的参数对应于要构建URL的变量。下面是几个例子:

  1. url_for('login') # 返回/login
  2. url_for('login',id='1') # 将id作为URL参数,返回/login?id=1
  3. url_for('hello',name='man') # 适配hello函数的name参数,返回/hello/man
  4. url_for('static',filename='style.css') # 静态文件地址,返回/static/style.css

image

image





3.6 静态文件位置

  一个Web应用的静态文件包括了JS、CSS、图片等,Flask的风格是将所有的静态文件放在“static”子目录下。并且在代码或模板中,使用 url_for('static') 来获取静态文件目录。
  如果你想改变这个静态目录的位置,你可以在创建应用时,指定 static_folder 参数。
  在3.5中第四个例子就是通过 url_for() 函数获取“static”目录下的的指定文件

  1. app = Flask(__name__, static_folder='files')





4. 模板

  在前面,我们讲到了Flask中的Controller和Model,但是一个完整的MVC,没有View怎么行?前端代码如果都靠后台拼接而成,就太麻烦了。本章节,我们介绍下Flask中的View,即模板。




4.1 模板例子

  Flask的模板功能是基于Jinja2模板引擎实现的。我们先来实现一个例子。创建一个新的Flask运行文件,代码如下:

  1. from flask import Flask
  2. from flask import render_template
  3. app = Flask(__name__)
  4. @app.route('/hello')
  5. @app.route('/hello/<name>')
  6. def hello(name=None):
  7. return render_template('hello.html', name=name)
  8. if __name__ == '__main__':
  9. app.run(host='0.0.0.0', debug=True)

image

  这段代码同上一章节中的多URL的例子非常相似,区别就是 hello() 函数并不是直接返回字符串,而是调用了 render_template() 方法来渲染模板。方法的第一个参数 hello.html 指向你想渲染的模板名称,第二个参数 name 是你要传到模板去的变量,变量可以传多个。
  
  那么这个模板 hello.html 在哪儿呢,变量参数又该怎么用呢?接下来我们创建模板文件,在当前目录下,创建一个子目录“templates”(注意,一定要用这个名字)。然后在“template”目录下创建文件“hello.html”,内容如下:

  1. <!doctype html>
  2. <title>Hello Sample</title>
  3. {% if name %}
  4. <h1>Hello {{name}}!</h1>
  5. {% else %}
  6. <h1>Hello World!</h1>
  7. {% endif %}

image

  这段很像HTML的代码,就是一个HTML模板,根据 name 变量的值,显示不同的内容。变量或表达式由 {{}} 修饰,而控制语句由 {% %} 修饰,其他的代码,就是我们常见的HTML。
  
  运行上面的代码,打开浏览器访问 http://127.0.0.1:5000/hello/man ,浏览器打开页面如下:

image

  果然,模板代码进入了 Hello {{name}}! 分支,并且变量 {{ name }} 被替换为了“man”,JinJa2的模板引擎还有更多强大的功能,包括for循环,过滤器等。模板里也可以直接访问内置对象,如:Request、Session等。




4.2 模板继承

  一般我们的网站虽然页面多,但是很多部分是重用的,比如页首、页脚、导航栏之类的。对于每个页面,都要写这些代码,很麻烦。Flask的Jinja2模板支持模板继承功能,省去了这些重复代码。让我们基于上面的例子,在“templates”目录下,创建一个名为“layout.html”的模板:

  1. <!doctype html>
  2. <title>Hello Sample</h1>
  3. <link rel="stylesheet" type="text/css" href="{{url_for('static', filename='style.css')}}">
  4. <div class="page">
  5. {% block body %}
  6. {% endblock %}
  7. </div>

image

  再修改之前的“hello.html”,把原来的代码定义在 {% block body %}中,并在代码一开始“继承”上面的“layout.html”:

  1. {% extends "layout.html" %}
  2. {% block body %}
  3. {% if name %}
  4. <h1>Hello {{name}}!</h1>
  5. {% else %}
  6. <h1>Hello World!</h1>
  7. {% endif %}
  8. {% endblock %}

  打开浏览器,再看下: http://127.0.0.1:5000/hello/man 页面的源码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Hello Sample</title>
  6. </head>
  7. <link rel="stylesheet" type="text/css" href="/static/style.css">
  8. <div class="page">
  9. <h1>Hello man!</h1>
  10. </div>
  11. </html>

  你会发现,虽然 render_template() 加载了“hello.html”模板,但是“layout.html”的内容也一起被加载上了。而且“hello.html”中的内容被放置在了“layout.html”中的 {block body} 位置上。形象的说,就是“hello.html”继承了“layout.html”。




4.3 HTML自动转义

  我们看下下面的代码:

  1. @app.route('/')
  2. def index():
  3. return '<div>Hello %s</div> % <em>Flask</em>'

  打开页面会看到“Hello Flask”的字样,而“Flask”是斜体的,因为我们加上了 <em> 标签。但有时我们并不想让这些HTML标签自动转义,特别是专递表单参数时,很容易导致HTML注入的漏洞。我们把上面的代码改一下,引入“Markup”类:

  1. from flask import Flask, Markup
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def index():
  5. return Markup('<div>Hello %s</div>') % '<em>Flask</em>'

  再次打开页面,<em> 标签显示在页面上了。Markup还有很多方法,比如 escape() 呈现HTML标签, striptags() 去除HTML标签。这里就不一一列举了。
  
  我们会在Flask紧接系列里对模板功能作更详细的介绍。




5. 请求、响应及会话

  一个完整的http请求,包括了客户端的请求Request,服务器端的响应Response,会话Session等。一个基本的Web框架一定会提供内建的对象来访问这些信息,Flask当然也不例外,我们来看看在Flask中怎么使用这些内建对象。




5.1 Flask内建对象

  引入flask保重的request对象,就可以直接在请求函数中直接使用该对象了。我们改进一下章节2中的 login() 方法:

  1. from flask import request
  2. @app.route('/login', methods=['POST','GET'])
  3. def login():
  4. if request.method == 'POST':
  5. if request.form['user'] == 'admin':
  6. return 'Admin login successfully!'
  7. else:
  8. return 'No such user!'
  9. title = request.args.get('title', 'Default')
  10. return render_template('login.html', title=title)

image

  在上一章节的“templates”目录下,添加“login.html”文件,内容如下:

  1. {% extends "layout.html"}
  2. {% block body %}
  3. <form name="login" action='/login' method="post">
  4. Hello {{ title }}, please login by:
  5. <input type="text" name="user" />
  6. </form>
  7. {% endblock %}

image

image

  
执行上面的例子,使用PostMan访问 http://127.0.0.1:5000/login ,结果如上图;简单解释下:



  request 的详细使用可参阅Flask的官方API文档。




5.2 会话对象Session

  会话可以用来保存当前请求的一些状态,以便于在请求之前共享信息。我们将上面的python代码改动下:

  1. from flask import request, session
  2. @app.route('/login', methods=['POST','GET'])
  3. def login():
  4. if request.method == 'POST':
  5. if request.form['user'] == 'admin':
  6. session['user'] = request.form['user']
  7. return 'Admin login successfully'
  8. else:
  9. return 'No such user!'
  10. if 'user' in session:
  11. return 'Hello %s!' % session['user']
  12. else:
  13. title = request.args.get('title', 'Default')
  14. return render_tamplate('login.html', title=title)
  15. app.secret_key = '123456'

image

image

  运行效果如上图;可以看到“admin”登陆成功之后,再打开“login”页面就不会出现表单了。session对象的操作就像一个字典一样。
  
  特别提醒: 使用session时,一定要设置一个密钥 app.secret_key ,如上例。否则运行会报错,错误内容大致是: RuntimeError: the session is unavilable because no secret 。密钥要尽量复杂,最好使用一个随机数,上面的例子不是一个好密钥。
  
  我们顺便写一个简单的登出的方法,清除字典里的键值:

  1. from flask import Flask, request, session, redirect, url_for
  2. @app.route('/logout')
  3. def logout():
  4. session.pop('user', None)
  5. return redirect(url_for('login'))

image




5.3 构建响应

  在之前的例子中,请求的响应我们都是直接返回字符串内容,或者通过模板来构建响应内容然后返回。其实我们也可以先构建响应对象,设置一些参数(比如响应头)后,再将其返回。修改下上面例子中的“Get”请求部分:

  1. ...
  2. else:
  3. title = request.args.get('title', 'Default')
  4. response = make_response(render_template('login.html', title=title),200)
  5. response.headers['key'] = 'value'
  6. return response
  7. ...

image

image

  F12打开浏览器调试,在“Get”请求用户未登录状态下,可以看到响应头中有一个 key 项。make_response 方法就是用来构建 response 对象的,第二个参数代表响应状态码,缺省就是“200”。 response 对象的详细使用可参阅Flask的官方APIA文档。




5.4 Cookie的使用

  提到了Session,当然也要介绍Cookie,毕竟没有Cookie,Session就根本没有用。Flask中使用Cookie也很简单:

  1. ...
  2. def login():
  3. response = None
  4. if request.method == 'POST':
  5. if request.form['user'] == 'admin':
  6. session['user'] = request.form['user']
  7. response = make_response('Admin login successfully!')
  8. response.set_cookie('login_time', time.strftime('%Y-%m-%d %H:%M:%S'))
  9. ...
  10. else:
  11. if 'user' in session:
  12. login_time = request.cookie.get('login_time')
  13. response = make_response('Hello %s, you logged in on %s' % (session['user'], login_rime))
  14. ...
  15. ...

image

  
  例子越来越长了,这次我们引入了 time 模块来获取当前系统时间。我们在返回响应时,通过 response.set_cookie() 函数来设置 Cookie 项,之后这个项值会被保存在浏览器中。这个函数的第三个参数 max_age 可以设置该Cookie项的有效期,单位是秒,不设的话,在浏览器关闭后,该Cookie项即失效。
  
  在请求中, request.cookies() 对象就是一个保存了浏览器Cookie的字典,使用其 get() 函数就可以获取相应的键值。




5.5 全局对象g

  flask.g 是Flask的一个全局对象,这里有点容易让人误解,其实 g 的作用范围,就在一个请求(也就是一个线程)里,它不能在多个请求间共享。你可以在 g 对象里保存任何你想保存的内容。一个最常用的例子,就是在进入请求前,保存数据库连接。这个我们会在介绍数据库集成的时候讲到。




6. 错误处理及消息闪现

  本章节将补充一些Flask的基本功能,包括错误处理,URL重定向,日志功能,还有一个很有趣的消息闪现功能。




6.1 错误处理

  使用 abort() 函数可以直接退出请求,返回错误代码:

  1. from flask import Flask, abort
  2. app = Flask(__name__)
  3. @app.route('/error')
  4. def error():
  5. abort(404)
  6. if __name__ == '__main__':
  7. app.run()

image

image

  上例会显示浏览器的404错误页面。有时候,我们想要在遇到特定错误代码时做些事情,或者重写错误页面,可以用下面的方法:

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

image

  此时,当再次遇到404错误时,即会调用 page_not_found() 函数,其返回“404.html”的模板页。第二个参数代表错误代码。
  
  不过,在实际开发过程中,我们并不会经常使用 abort() 来退出,常用的错误处理方法一般都是异常的抛出或捕获。装饰器 @app.errorhandler() 除了可以注册错误代码外,还可以注册指定的异常类型。让我们定义一个异常:
  

  1. class InvalidUsage(Exception):
  2. status_code 400
  3. def __int__(self,message,status_code=400):
  4. Exception.__int__(self)
  5. self.message = message
  6. self.status_code = status_code
  7. @app.errorhandler(InvalidUsage)
  8. def invalid_usage(error):
  9. response = make_response(error.message)
  10. response.state_code = error.status_code
  11. return response

  
  我们在上面的代码中定义了一个异常 InvalidUsage ,同时我们通过装饰器 @app.errhandler()
修饰了函数 invalid_usage() ,装饰器中注册了我们刚刚定义的异常类。这也就意味着,一旦遇到 InvalidUsage 异常被抛出,这个 incalid_usage() 函数就会被调用。写个路由试一下:
  

  1. @app.route('/exception')
  2. def exception():
  3. raise InvalidUsage('No privilege to access the resource', status_code = 403)

image

image





6.2 重定向

  重定向 redirect() 函数的使用,在章节5.2 logout 的例子中已有出现。作用就是当客户端浏览某个网址时,将其导向另一个网址。常见的例子,比如用户在未登录时浏览某个需授权的页面,我们将其重定向到登录页要求其先登录。

  1. from flask import Flask
  2. from flask import session, redirect
  3. @app.route('/')
  4. def index():
  5. if 'user' in session:
  6. return 'Hello $s!' % session['user']
  7. esle:
  8. return redirect(url_for('login.html'),302)

  redirect() 第二个参数是HTTP状态码,可取的值有301,302,303,305和307,默认即302。




6.3 日志

  提到错误处理,那一定要说日志。Flask提供 logger 对象,它是一个标准的 Python Logger 类。修改上面例子中的 exception() 函数:

  1. @app.route('/exception')
  2. def exception():
  3. app.logger.debug('Enter exception method')
  4. app.logger.error('403 error happened')
  5. raise InvalidUsage('No privilege to access the resource', status_code=403)

image

image

  
  执行后,可以在控制台看到日志信息(如上图)。在debug模式下,日志会默认输出到标准错误stderr中。你可以添加 FileHandler 来使其输出到日志文件中,也可以修改日志的记录格式,下面演示一个简单的日志配置代码:
  

  1. server_log = TimedRotatingFileHandler('server.log', 'D')
  2. serevr_log.setLevel(logging.DEBUG)
  3. server_log.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
  4. error_log = TimedRotatingFilehandler('error.log', 'D')
  5. error_log.setLevel(logging.DEBUG)
  6. error_log.setFormatter(logging.Formatter('%(asctime)s: %(message)s [in %(pathname)s:%(lineno)d]'))
  7. app.logger.addHandler(server_log)
  8. app.logger.addHandler(error_log)

image

  
  添加上述代码并执行后,会发现在*.py执行文件同目录下生成了两个日志文件:error.logserver.log ;打开查看内容分别为:

  1. # server.log
  2. 2019-12-21 10:11:00,467 ERROR: 403 error happened
  3. # error.log
  4. 2019-12-21 10:11:00,467: 403 error happened [in F:\1. Code\1. Python\flasktest\basetest.py:39]

  
  上例中,我们在本地目录下创建了两个日志文件,分别是:
  server.log : 记录所有级别日志;
  error.log : 只记录错误日志。
  我们分别给两个文件不同的内容格式。另外,我们使用了 TimedRotatingFileHandler 并给了参数 D ,这样日志会每天创建一个新文件,并将旧文件加日期后缀来归档。
  

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注