Web框架
Flask
虚拟环境
相关配置参数
路由
视图
正则匹配路由
异常捕获
请求勾子
request
状态保持
上下文
Flask-Script 扩展
什么是Web框架?
为什么要用Web框架?
web网站发展至今,特别是服务器端,涉及到的知识、内容,非常广泛。这对程序员的要求会越来越高。如果采用成熟,稳健的框架,那么一些基础的工作,比如,安全性,数据流控制等都可以让框架来处理,那么程序开发人员可以把精力放在具体的业务逻辑上面。使用框架的优点:
总结一句话:避免重复造轮子
在 Python 中常用的 Web 框架有
Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。
其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
Werkzeug是一个遵循WSGI协议的python函数库
- 其内部实现了很多Web框架底层的东西,比如request和response对象;
- 与WSGI规范的兼容;支持Unicode;
- 支持基本的会话管理和签名Cookie;
- 集成URL请求路由等。
Werkzeug库的 routing 模块负责实现 URL 解析。不同的 URL 对应不同的视图函数,routing模块会对请求信息的URL进行解析,匹配到URL对应的视图函数,执行该函数以此生成一个响应信息。
routing模块内部有:
Flask常用扩展包:
扩展列表:http://flask.pocoo.org/extensions/
为什么要搭建虚拟环境?
在开发过程中, 当需要使用python
的某些工具包/框架时需要联网安装
flask-0.10.1
版本
sudo pip install flask==0.10.1
提示:使用如上命令, 会将flask-0.10.1
安装到/usr/local/lib/python2.7/dist-packages
路径下
问题:如果在一台电脑上, 想开发多个不同的项目, 需要用到同一个包的不同版本, 如果使用上面的命令, 在同一个目录下安装或者更新, 新版本会覆盖以前的版本, 其它的项目就无法运行了.
解决方案 : 虚拟环境
虚拟环境
可以搭建独立的python运行环境
, 使得单个项目的运行环境与其它项目互不影响.虚拟环境
都位于/home/
下的隐藏目录.virtualenvs
下
如何搭建虚拟环境?
安装虚拟环境的命令 :
sudo pip install virtualenv
sudo pip install virtualenvwrapper
安装完虚拟环境后,如果提示找不到mkvirtualenv命令,须配置环境变量:
# 1、创建目录用来存放虚拟环境
mkdir
$HOME/.virtualenvs
# 2、打开~/.bashrc文件,并添加如下:
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
# 3、运行
source ~/.bashrc
创建虚拟环境的命令 :
在python2中,创建虚拟环境
mkvirtualenv 虚拟环境名称
例 :
mkvirtualenv py_flask
在python3中,创建虚拟环境
mkvirtualenv -p python3 虚拟环境名称
例 :
mkvirtualenv -p python3 py3_flask
提示 :
如何使用虚拟环境?
查看虚拟环境的命令 :
workon 两次tab键
使用虚拟环境的命令 :
workon 虚拟环境名称
例 :使用python2的虚拟环境
workon py_flask
例 :使用python3的虚拟环境
workon py3_flask
退出虚拟环境的命令 :
deactivate
删除虚拟环境的命令 :
rmvirtualenv 虚拟环境名称
例 :删除虚拟环境py3_flask
先退出:deactivate
再删除:rmvirtualenv py3_flask
如何在虚拟环境中安装工具包?
提示 : 工具包安装的位置 :
~/.virtualenvs/py_flask/lib/python2.7/site-packages/
~/.virtualenvs/py3_flask/lib/python3.5/site-packages
python3版本下安装flask-0.10.1的包 :
pip install 包名称
例 : 安装flask-0.10.1的包
pip install flask==0.10.1
pip freeze
创建 Python 项目
Pure Python
类型的项目,创建项目完成之后选择之前创建的scrats_flask
作为虚拟环境示例:
新建文件helloworld.py
from flask import Flask
# Flask函数接收一个参数__name__,它会指向程序所在的包
app = Flask(__name__)
# 装饰器的作用是将路由映射到视图函数 index
@app.route('/')
def index():
return 'Hello world'
# Flask应用程序实例的 run 方法 启动 WEB 服务器
if __name__ == '__main__':
app.run()
就酱紫:
上面简短的实现了Flask 的 hello world,下面对Flask 程序的创建、运行配置进一步了解。
初始化参数:
Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块),接下来就来详细查看一下 Flask 应用程序在创建的时候一些需要我们关注的参数:
__name__
就可以/ + static_folder
static
templates
程序加载配置
在 Flask 程序运行的时候,可以给 Flask 设置相关配置,比如:配置 Debug 模式,配置数据库连接地址等等,设置 Flask 配置有以下三种方式:
以下演练以设置应用程序的 DEBUG(调试模式) 为例,设置应用为调式模式这后,可以实现以下功能:
- 程序代码修改后可以自动重启服务器
- 在服务器出现相关错误的时候可以直接将错误信息进行抛出到控制台打印
配置对象
# 配置对象,里面定义需要给 APP 添加的一系列配置
class Config(object):
DEBUG = True
# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 从配置对象中加载配置
app.config.from_object(Config)
运行测试,在修改代码之后直接保存,会自动重启服务器
配置文件
config.ini
,在配置文件中添加配置
# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 从配置文件中加载配置
app.config.from_pyfile('config.ini')
app.run的参数
app.run(host="0.0.0.0", port=5000, debug = True)
指定路由地址
# 指定访问路径为 exp1
@app.route('/exp1')
def demo1():
return 'exp1'
给路由传参示例
有时我们需要将同一类 URL 映射到同一个视图函数处理,比如:使用同一个视图函数来显示不同用户的个人信息。
# 路由传递参数
@app.route('/user/')
def user_info(user_id):
return 'hello %s' % user_id
# 路由传递参数
@app.route('/user/')
def user_info(user_id):
return 'hello %d' % user_id
这里指定int,尖括号中的内容是动态的,在此暂时可以理解为接受 int 类型的值,实际上 int 代表使用 IntegerConverter 去处理 url 传入的参数
在 Flask 中,定义一个路由,默认的请求方式为:
如果想添加请求方试,那么可以如下指定:
@app.route('/exp2', methods=['GET', 'POST'])
def exp2():
# 直接从请求中取到请求方式并返回
return request.method
exp2 请求方式为:
返回JSON
在使用 Flask 写一个接口时候需要给客户端返回 JSON 数据,在 Flask 中可以直接使用 jsonify 生成一个 JSON 的响应
# 返回JSON
@app.route('/demo4')
def exp4():
json_dict = {
"user_id": 110,
"user_name": "kenan"
}
return jsonify(json_dict)
不推荐使用 json.dumps 转成 JSON 字符串直接返回,因为返回的数据要符合 HTTP 协议规范,如果是 JSON 需要指定 content-type:application/json
重定向
# 重定向
@app.route('/exp5')
def exp5():
return redirect('http://www.baidu.com')
@app.route('/exp1')
def exp1():
return 'exp1'
# 重定向
@app.route('/exp5')
def exp5():
return redirect(url_for('exp1'))
# 路由传递参数
@app.route('/user/')
def user_info(user_id):
return 'hello %d' % user_id
# 重定向
@app.route('/exp5')
def exp5():
# 使用 url_for 生成指定视图函数所对应的 url
return redirect(url_for('user_info', user_id=100))
自定义状态码
@app.route('/exp6')
def exp6():
return '状态码为 666', 666
在 web 开发中,会出现限制用户访问规则的场景,这个时候就需要用到正则匹配,根据自己的规则去限定请求参数再进行访问
具体实现步骤为:
代码实现
from werkzeug.routing import BaseConverter
# 自定义正则转换器
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 将接受的第1个参数当作匹配规则进行保存
self.regex = args[0]
app = Flask(__name__)
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re
app.url_map.converters['re'] = RegexConverter
@app.route('/user/')
def user_info(user_id):
return "user_id 为 %s" % user_id
运行测试:http://127.0.0.1:5000/user/1a2 ,如果访问的url不符合规则,会提示找不到页面
系统自带转换器
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
系统自带的转换器具体使用方式在每种转换器的注释代码中有写,请留意每种转换器初始化的参数。
HTTP 异常主动抛出
# abort(404)
abort(500)
抛出状态码的话,只能抛出 HTTP 协议的错误状态码
捕获错误
@app.errorhandler(500)
def internal_server_error(e):
return '服务器搬家了'
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除数不能为0'
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
代码测试
from flask import Flask
from flask import abort
app = Flask(__name__)
# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
print("before_first_request")
# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
print("before_request")
# if 请求不符合条件:
# return "laowang"
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(e):
print("teardown_request")
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
before_first_request
before_request
after_request
teardown_request
before_request
after_request
teardown_request
request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)
常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
示例
@app.route('/', methods=['POST'])
def index():
pic = request.files.get('pic')
pic.save('./static/a.png')
return 'index'
Cookie
Session
无状态协议:
- 协议对于事务处理没有记忆能力
- 对同一个 url 请求没有上下文关系
- 每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
- 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器
- 人生若只如初见
提示:
当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息
from flask imoprt Flask,make_response
@app.route('/cookie')
def set_cookie():
resp = make_response('this is to set cookie')
resp.set_cookie('username', 'scrat')
return resp
@app.route('/cookie')
def set_cookie():
response = make_response('hello world')
response.set_cookie('username', 'scrat', max_age=3600)
return response
from flask import Flask,request
#获取cookie
@app.route('/request')
def resp_cookie():
resp = request.cookies.get('username')
return resp
Session
session数据的获取
session:请求上下文对象,用于处理http请求中的一些数据内容
@app.route('/index1')
def index1():
session['username'] = 'scrat'
return redirect(url_for('index'))
@app.route('/')
def index():
return session.get('username')
记得设置secret_key: app.secret_key = 'itheima' secret_key的作用:https://segmentfault.com/q/1010000007295395
上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
Flask中有两种上下文,请求上下文和应用上下文
思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等
在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
应用上下文对象有:current_app,g
current_app
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
current_app.name
current_app.test_value='value'
g变量
g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
g.name='abc'
注意:不同的请求,会有不同的全局变量
两者区别:
上下文中的对象只能在指定上下文中使用,超出范围不能使用 请求上下文和应用上下文原理实现:https://segmentfault.com/a/1190000004223296
通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,比如我们可以通过:
python hello.py runserver -host ip地址
以上代码告诉服务器在哪个网络接口监听来自客户端的连接。默认情况下,服务器只监听来自服务器所在的计算机发起的连接,即localhost连接。
代码实现
pip install flask-script
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
# 把 Manager 类和应用程序实例进行关联
manager = Manager(app)
@app.route('/')
def index():
return '只因你太美'
if __name__ == "__main__":
manager.run()
Flask-Script 还可以为当前应用程序添加脚本命令。