Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。
flask中虚拟环境的搭建(参考博客)
1 sudo pip install virtualenv 2 sudo pip install virtualenvwrapper 3 4 # 1、创建目录用来存放虚拟环境 5 mkdir 6 $HOME/.virtualenvs 7 8 # 2、打开~/.bashrc文件,并添加如下: 9 export WORKON_HOME=$HOME/.virtualenvs 10 source /usr/local/bin/virtualenvwrapper.sh 11 12 # 3、运行 13 source ~/.bashrc 14 15 创建虚拟环境 16 mkvirtualenv 虚拟环境名称 17 例 : 18 mkvirtualenv py_flask 19 20 mkvirtualenv -p python3 虚拟环境名称 21 例 : 22 mkvirtualenv -p python3 py3_flask 23 24 进入虚拟环境 25 workon 虚拟环境名称 26 27 例 :使用python2的虚拟环境 28 workon py_flask 29 30 例 :使用python3的虚拟环境 31 workon py3_flask 32 33 退出虚拟环境 34 deactivate 35 36 rmvirtualenv 虚拟环境名称 37 38 例 :删除虚拟环境py3_flask 39 40 先退出:deactivate 41 再删除:rmvirtualenv py3_flask 42 43 在虚拟环境中安装包 44 pip install 包名称
一,flask的基本使用:
1 from flask import Flask 2 3 app = Flask(__name__) 4 5 6 @app.route('/') 7 def hello_world(): 8 return 'Hello World!' 9 10 11 if __name__ == '__main__': 12 app.run(debug=True)
二,基本的配置
1 # default_config = ImmutableDict({ 2 # 'DEBUG': False, 3 # 'TESTING': False, 4 # 'PROPAGATE_EXCEPTIONS': None, 5 # 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 6 # 'SECRET_KEY': None, 7 # 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 8 # 'USE_X_SENDFILE': False, 9 # 'LOGGER_NAME': None, 10 # 'SERVER_NAME': None, 11 # 'APPLICATION_ROOT': None, 12 # 'SESSION_COOKIE_NAME': 'session', 13 # 'SESSION_COOKIE_DOMAIN': None, 14 # 'SESSION_COOKIE_PATH': None, 15 # 'SESSION_COOKIE_HTTPONLY': True, 16 # 'SESSION_COOKIE_SECURE': False, 17 # 'MAX_CONTENT_LENGTH': None, 18 # 'SEND_FILE_MAX_AGE_DEFAULT': 12 * 60 * 60, # 12 hours 19 # 'TRAP_BAD_REQUEST_ERRORS': False, 20 # 'TRAP_HTTP_EXCEPTIONS': False, 21 # 'PREFERRED_URL_SCHEME': 'http', 22 # 'JSON_AS_ASCII': True, 23 # 'JSON_SORT_KEYS': True, 24 # 'JSONIFY_PRETTYPRINT_REGULAR': True, 25 # }) 26 27 28 29 方式一 30 创建 Flask 类的对象,指向程序所在的包的名称 31 app = Flask(__name__) 32 33 # 从配置文件中加载配置 34 app.config.from_pyfile('config.ini') 35 36 方式二: 37 # 配置对象,里面定义需要给 APP 添加的一系列配置 38 class Config(object): 39 # DEBUG = True 40 41 42 # 创建 Flask 类的对象,指向程序所在的包的名称 43 app = Flask(__name__) 44 45 # 从配置对象中加载配置 46 app.config.from_object(Config)
三,路由系统
1 DEFAULT_CONVERTERS = { 2 'default': UnicodeConverter, 3 'string': UnicodeConverter, 4 'any': AnyConverter, 5 'path': PathConverter, 6 'int': IntegerConverter, 7 'float': FloatConverter, 8 'uuid': UUIDConverter, 9 }
路由关系的创建
1 from flask import Flask 2 from flask import request 3 4 app = Flask(__name__) 5 6 7 # 指定访问路径为 demo1 8 @app.route('/demo1') 9 def demo1(): 10 return 'demo1' 11 12 13 # 路由传递的参数默认当做 string 处理,也可以指定参数的类型 14 @app.route('/user/<int:user_id>') 15 def user_info(user_id): 16 return 'hello %d' % user_id 17 18 19 @app.route('/demo2', methods=['GET', 'POST']) 20 def demo2(): 21 # 直接从请求中取到请求方式并返回 22 return request.method 23 24 25 if __name__ == '__main__': 26 app.run(debug=True)
1 from werkzeug.routing import BaseConverter 2 from flask import Flask 3 4 5 # 自定义正则转换器 6 class RegexConverter(BaseConverter): 7 def __init__(self, url_map, *args): 8 super(RegexConverter, self).__init__(url_map) 9 # 将接受的第1个参数当作匹配规则进行保存 10 self.regex = args[0] 11 12 13 app = Flask(__name__) 14 15 # 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re 16 app.url_map.converters['re'] = RegexConverter 17 18 19 @app.route('/user/<re("[0-9]{3}"):user_id>') 20 def user_info(user_id): 21 return "user_id 为 %s" % user_id 22 23 24 if __name__ == '__main__': 25 app.run(debug=True)
1 from werkzeug.routing import BaseConverter 2 from flask import Flask 3 4 5 # 自定义正则转换器 6 class RegexConverter(BaseConverter): 7 def __init__(self, url_map, *args): 8 super(RegexConverter, self).__init__(url_map) 9 # 将接受的第1个参数当作匹配规则进行保存 10 self.regex = args[0] 11 12 def to_python(self, value): 13 return int(value) 14 15 16 app = Flask(__name__) 17 18 # 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re 19 app.url_map.converters['re'] = RegexConverter 20 21 22 @app.route('/user/<re("[0-9]{3}"):user_id>') 23 def user_info(user_id): 24 print("user_id的类型为", type(user_id)) 25 return "user_id 为 %s" % user_id 26 27 28 if __name__ == '__main__': 29 app.run(debug=True)
1 to_url: 2 在使用 url_for 去获取视图函数所对应的 url 的时候,会调用此方法对 url_for 后面传入的视图函数参数做进一步处理 3 具体可参见 Flask 的 app.py 中写的示例代码:ListConverter
视图的常用逻辑
不推荐使用 json.dumps 转成 JSON 字符串直接返回,因为返回的数据要符合 HTTP 协议规范,
如果是 JSON 需要指定 content-type:application/json
1 from flask import Flask 2 from flask import jsonify 3 4 app = Flask(__name__) 5 6 7 # 返回JSON 8 @app.route('/demo4') 9 def demo4(): 10 json_dict = { 11 "user_id": 10, 12 "user_name": "laowang" 13 } 14 return jsonify(json_dict) 15 16 17 if __name__ == '__main__': 18 app.run(debug=True) 19 20 21 # 不推荐使用 json.dumps 转成 JSON 字符串直接返回,因为返回的数据要符合 HTTP 协议规范, 22 # 如果是 JSON 需要指定 content-type:application/json
重定向
重定向到指定的网址
1 from flask import Flask 2 from flask import redirect 3 4 app = Flask(__name__) 5 6 7 @app.route('/demo5') 8 def demo5(): 9 return redirect('http://www.itheima.com') 10 11 12 if __name__ == '__main__': 13 app.run(debug=True)
重定向到自己写的函数中
1 @app.route('/demo1') 2 def demo1(): 3 return 'demo1' 4 5 @app.route('/demo5') 6 def demo5(): 7 return redirect(url_for('demo1'))
重定向到带有参数的路由函数中
1 # 路由传递参数 2 @app.route('/user/<int:user_id>') 3 def user_info(user_id): 4 return 'hello %d' % user_id 5 6 7 # 重定向 8 @app.route('/demo5') 9 def demo5(): 10 # 使用 url_for 生成指定视图函数所对应的 url 11 return redirect(url_for('user_info', user_id=100))
自定义状态码
1 @app.route('/demo6') 2 def demo6(): 3 return '状态码为 666', 666
异常的捕获
1 from flask import Flask 2 from flask import abort 3 4 app = Flask(__name__) 5 6 7 @app.route('/') 8 def hello_world(): 9 return 'Hello World!' 10 11 12 @app.route("/demo1") 13 def demo1(): 14 # 主动http状态码 15 abort(404) 16 return "demo1" 17 18 19 # 使用装饰器来捕获异常指定的状态码和异常 20 @app.errorhandler(404) 21 def page_not_fund(error): 22 print(error) 23 return "页面不存在" 24 25 26 @app.route("/demo2") 27 def div_demo2(): 28 a = 5 29 b = 0 30 return a / b 31 32 33 # 使用错误捕获来捕获除数不能为0的的错误 34 @app.errorhandler(ZeroDivisionError) 35 def Zero_Division_Error(error): 36 print(error) 37 return "除数不能为0" 38 39 40 # 第二种捕获异常的方式 41 # def page_not_found(error): 42 # return '你访问的页面不存在', 404 43 # app.error_handler_spec[None][404] = page_not_found 44 45 46 47 if __name__ == '__main__': 48 app.run(debug=True)
注册一个处理程序错误的装饰器,当程序抛出指定错误状态码的时候会调用装饰器所装饰的方法(除了捕获异常的状态码之外还可以捕获指定的异常)
flask中的请求钩子
1 #! /usr/bin/env python 2 # *-* coding: utf-8 *-* 3 4 from flask import Flask 5 6 app = Flask(__name__) 7 8 9 @app.before_first_request 10 def before_first_request(): 11 print("before_first_request") 12 13 14 @app.before_request 15 def before_request(): 16 print("before_request") 17 18 19 @app.after_request 20 def after_request(response): 21 print("response", response) 22 print("after_request") 23 return response 24 25 26 # 在请求之后可以被执行,如果有异常会将此异常传入到这个函数来做处理 27 @app.teardown_request 28 def teardown_request(error): 29 print(error) 30 print("teardown_request") 31 32 33 @app.route('/') 34 def index(): 35 print("index", index) 36 return 'index' 37 38 39 if __name__ == '__main__': 40 app.run(debug=True)
before_first_request(在处理第一个请求前执行,后续的执行过程就不会再执行了)
before_request(每次发起请求前都会执行,如果在某个修饰函数中返回一个响应,视图函数将不再被调用)
after_request
(
没有抛出异常,则在每次请求后会执行
接收一个参数:视图函数做出的响应;
在此函数中可以对视图函数做出的响应做最后一步的处理;
需要将参数中的响应在此函数中返回
)
teardown_request
(
每次请求之后都会执行;
接收一个参数:如果抛出异常,则参数会捕捉到这个异常;
)
1 #! /usr/bin/env python 2 # *-* coding: utf-8 *-* 3 4 from flask import Flask,request 5 from werkzeug.serving import run_simple 6 7 app = Flask(__name__) 8 9 10 @app.route('/') 11 def hello_world(): 12 return 'Hello World!' 13 14 @app.route('/index', methods=["POST",]) 15 def index(): 16 print(request.method) 17 return 'index index!' 18 19 # rule 视图函数和酷游关系的映射关系 20 # map rule的集合 21 # BaseConverter匹配路由匹配的规则 22 # MapAdapter协调以上的路由匹配的工作 23 24 25 if __name__ == '__main__': 26 print(app.url_map) 27 app.run(debug=True)
Werkzeug库的 routing 模块负责实现 URL 解析。不同的 URL 对应不同的视图函数,routing模块会对请求信息的URL进行解析,匹配到URL对应的视图函数,执行该函数以此生成一个响应信息。
routing模块内部有:
-
- Rule类
- 用来构造不同的URL模式的对象,路由URL规则
- Map类
- 存储所有的URL规则和一些配置参数
- BaseConverter的子类
- 负责定义匹配规则
- MapAdapter类
- 负责协调Rule做具体的匹配的工作
- Rule类
请求上下文(request context)
1 #! /usr/bin/env python 2 # *-* coding: utf-8 *-* 3 4 5 from flask import Flask 6 from flask import request 7 8 app = Flask(__name__) 9 10 11 @app.route('/index', methods=["GET", "POST"]) 12 def index(): 13 return 'Hello World!' 14 15 16 @app.route("/args", methods=["GET", "POST"]) 17 def args(): 18 # get方式地址栏传递参数 19 print(request.form.get("name")) 20 print(request.form.get("age")) 21 # post方式请求体中传递参数 22 print(request.args.get("name")) 23 print(request.args.get("age")) 24 return "success" 25 26 27 @app.route("/file", methods=["POST"]) 28 def up_load_file(): 29 print(request.method) 30 pic = request.files.get("pic") 31 pic.save("./static/aaaa.png") 32 return "success" 33 34 35 if __name__ == '__main__': 36 app.run(debug=True)
request是flask中代表当前请求的request对象,是一个全局的请求上下文的一个变量(可以理解成为一个全局变量,在视图函数中可以直接使用,可以取到当前请求)。
备注:在地址栏中请求数据的方式可以用args的方式去在request中取值,如果在form表单中的数据需要在form中取值。
cookie的设置
1 #! /usr/bin/env python 2 # *-* coding: utf-8 *-* 3 4 from flask import Flask 5 from flask import request 6 from flask import make_response 7 8 app = Flask(__name__) 9 10 11 # 获取cookies 12 @app.route('/index') 13 def index(): 14 username = request.cookies.get("username") 15 user_id = request.cookies.get("user_id") 16 return '%s---%s' % (username, user_id) 17 18 19 # 设置cookies 20 @app.route("/login") 21 def login(): 22 response = make_response("success") 23 response.set_cookie("username", "xiaoimng", max_age=3600) 24 response.set_cookie("user_id", "1", max_age=3600) 25 return response 26 27 28 # 删除cookies 29 @app.route("/logout") 30 def logout(): 31 32 response = make_response("删除cookies成功") 33 response.delete_cookie("user_id") 34 response.delete_cookie("username") 35 return response 36 37 38 if __name__ == '__main__': 39 app.run(debug=True)
备注:
1,cookie的设置是在response中进行设置,在request中取值;
2,http是一种无状态的协议,浏览器在请求服务器的时候是无状态的,
无状态的原因是:
浏览器与服务器通过socket进行通信,服务器会将请求结果返回给浏览器,请求完毕后服务器都会将通信套接字关闭,并且服务器在关闭套接字之后就会销毁请求的页面的对象;
有时需要保持浏览器的状态,比如用户的登录状态,和浏览过的信息等;
无状态协议
1,协议对事物的处理没有记忆的功能;
2,对于同一个url请求没有上下文的关系;
3,每次执行的结果都是独立的,它执行的结果和情况与上前面的请求和之后的请求没有直接的关系,它不会受前面的请求应答的影响,也不会对后面的请求应答产生影响;
4,服务器没有保持客户端的状态,客户端每次需要自己将状态带到服务区端;
实现状态保持有两种方式;
1,在客户端设置cookie,在每次发送请求的时候将cookie带给服务器端;
2,在服务器端设置session,每次客户福安发送来请求之后,将客户端的cookie与session做做对比;(所有说session是依赖于cookie的,cookie可以单独存在session不可以独立存在);
1 #! /usr/bin/env python 2 # *-* coding: utf-8 *-* 3 from datetime import timedelta 4 from flask import Flask 5 from flask import session 6 7 app = Flask(__name__) 8 app.config["SECRET_KEY"] = "abcdefg" 9 10 11 # app.secret_key["SECRET_KEY"] = "abcsefght" 12 13 @app.route('/index') 14 def index(): 15 user_name = session.get("user_name") 16 user_id = session.get("user_id") 17 return "%s---%s" % (user_name, user_id) 18 19 20 @app.route("/login") 21 def login(): 22 session.permanent = True 23 app.permanent_session_lifetime = timedelta(second=10) 24 session["user_name"] = "xiaomingh" 25 session["user_id"] = "1" 26 return "session设置成功" 27 28 29 @app.route("/logout") 30 def logout(): 31 session.pop("user_name", None) 32 session.pop("user_id", None) 33 return "session删除删除成功" 34 35 36 if __name__ == '__main__': 37 app.run(debug=True)
思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等
在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session
-
- request
- 封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
- session
- 用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。
- request
应用上下文(application context)
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
应用上下文对象有:current_app,g
current_app
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
-
- 应用的启动脚本是哪个文件,启动时指定了哪些参数
- 加载了哪些配置文件,导入了哪些配置
- 连了哪个数据库
- 有哪些public的工具类、常量
- 应用跑再哪个机器上,IP多少,内存多大
current_app.name
current_app.test_value='value'
g变量
g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
flask_script
1 #! /usr/bin/env python 2 # *-* coding: utf-8 *-* 3 4 5 from flask import Flask 6 # 请求上下文的变量 7 from flask import request, make_response 8 # 运用上下文的变量 9 from flask_script import Manager 10 from flask import current_app 11 from flask import g 12 13 app = Flask(__name__) 14 app.debug = True 15 manager = Manager(app) 16 17 18 @app.route('/') 19 def hello_world(): 20 # 类似于一个全局变量的东西 21 print(current_app.config.get("DEBUG")) 22 return 'Hello World!' 23 24 25 if __name__ == '__main__': 26 manager.run() 27 # app.run(debug=True)
通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,比如我们可以通过:
python hello.py runserver -host ip地址