Flask
1, flask简介
2, flask四种主要工具
4, flask配置文件
5, flask路由
6, fbv讲解
7, fbv常用模式
8, flask之session
9, 闪现
10, 请求扩展
11, 中间件
*******************************************************************************************************************************************************************************
1flask简介
百度百科:Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用.
nginx:是一个高性能的HTTP和服务器/反向代理WEB服务器以及电子邮件(IMAP/POP3)代理服务器, 占用内存少,并发能力强,特别是在网页服务器表现非常好.国内知名大厂都在使用.
wsgiref: 前端与后端之间数据传输就是借助wsgiref模块传输. 在TCP协议中,它把应用层传输之间的协议封装起来, 直接调用此工具栏进行数据传输.一个简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块
from wsgiref.simple_server import make_server def mya(environ, start_response): print(environ) start_response('200 OK', [('Content-Type', 'text/html')]) if environ.get('PATH_INFO') == '/index': with open('index.html','rb') as f: data=f.read() elif environ.get('PATH_INFO') == '/login': with open('login.html', 'rb') as f: data = f.read() else: data=b'<h1>Hello, web!</h1>' return [data] if __name__ == '__main__': myserver = make_server('', 8011, mya) print('监听8010') myserver.serve_forever() wsgiref简单应用
werkzeug: Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等
from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 4000, hello)
2, flask安装
pip3 install flask
flask快速使用
zfrom flask import Flask app = Flask(__name__) # 将 '/'和视图函数hello_workd的对应关系添加到路由中 @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() # 最终调用了run_simple()
3, flask四种工具
1) render_template
返回一个HTML页面. 视图函数返回,调用:render_template('page.html'). 需要建一个templents文件夹, 将需要返回的HTML文档放入, 返回页面时, 它会自动在templates文件夹中查询,在页面中传入传输数据参数,需要经行关键字传参.在页面中使用模板语法直接调用,并且支持python多种数据操作.
2)redirect
实现页面跳转, 从一个页面跳转到另一个页面.redirect('page.html')
3)jsonify
将直接返回到前端数据经行json格式发, 返回给前端页面jsonify({'data': 'msg'})
4)
直接返回给前端字符串.
4, flask配置文件
方式一
app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
方式二
#通过py文件配置 app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") #通过环境变量配置 app.config.from_envvar("环境变量名称") #app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG': True}) 字典格式 app.config.from_object("python类或类的路径") app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录(Flask对象init方法的参数)
5, 路由系统
路由写法
@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
默认转换器
分组配置时前端传入的参数配置
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
路由分析
1. decorator = app.route('/',methods=['GET','POST'],endpoint='n1') def route(self, rule, **options): # app对象 # rule= / # options = {methods=['GET','POST'],endpoint='n1'} def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator 2. @decorator decorator(index) """ #同理 def login(): return '登录' app.add_url_rule('/login', 'n2', login, methods=['GET',"POST"]) #与django路由类似 #django与flask路由:flask路由基于装饰器,本质是基于:add_url_rule #add_url_rule 源码中,endpoint如果为空,endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)
6, fbv讲解
fbv: Function base view, 一个ur(路由)l对应一个视图函数.
cbv: Class base view, 一个url(路由)对应一个视图类.
cbv模型
from flask import Flask # 实例化产生一个Flask对象 app = Flask(__name__) # 将 '/'和视图函数hello_workd的对应关系添加到路由中 @app.route('/') # 1. v=app.route('/') 2. v(hello_world) def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() # 最终调用了run_simple()
fbv模型
from flask import Flask,views app = Flask(__name__) class Test(views.View): methods = ['GET', ] def dispatch_request(self): print('dispatch_request') return 'ok' app.add_url_rule('/index', view_func=Test.as_view(name='get')) if __name__ == '__main__': app.run()
7, 请求响应
from flask import Flask from flask import request from flask import render_template from flask import redirect from flask import make_response app = Flask(__name__) @app.route('/login.html', methods=['GET', "POST"]) def login(): # 请求相关信息 # request.method 提交的方法 # request.args get请求提及的数据 # request.form post请求提交的数据) # request.values post和get提交的数据总和 # request.cookies 客户端所带的cookie # request.headers 请求头 # request.path 不带域名,请求路径 # request.full_path 不带域名,带参数的请求路径 # request.script_root # request.url 带域名带参数的请求路径 # request.base_url 带域名请求路径 # request.url_root 域名 # request.host_url 域名 # request.host 127.0.0.1:500 # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename)) # 响应相关信息 # return "字符串" # return render_template('html模板路径',**{}) # return redirect('/index.html') # return jsonify({'k1':'v1'}) # response = make_response(render_template('index.html')) # response是flask.wrappers.Response类型 # response.delete_cookie('key') # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # return response return "内容" if __name__ == '__main__': app.run()
8, flask之session
flask中session源码执行流程主要是save_session和open_session两个方向
-save_seesion
-响应的时候,把session中的值加密序列化放大到了cookie中,返回到浏览器中
-open_session
-请求来了,从cookie中取出值,反解,生成session对象,以后再视图函数中直接用sessoin就可以了。
cookie:存放在客户端的键值对 session:存放在客户端的键值对 token:存放在客户端,通过算法来校验
使用session之前必须现在设置一下密钥
app.secret_key="asdas" #值随便
除请求对象之外,还有一个 session 对象。这个session对象是全局对象. 它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。 (app.session_interface对象)
设置:session['username'] = 'xxx' #在django中发什么三件事,1,生成一个随机的字符串 2 往数据库存 3 写入cookie返回浏览器 #在flask中他没有数据库,但session是怎样实现的? # 生成一个密钥写入这个cookie,然后下次请求的时候,通过这个cookie解密,然后赋值给session #我们通过app.session_interface来查看 删除:session.pop('username', None)
app.session_interface中save_session的参数(设置cookie的参数)
key, 键 value='', 值 max_age=None, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是 None`` ,这个cookie会延续到浏览器关闭为止 expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。 domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取 secure=False, 浏览器将通过HTTPS来回传cookie httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
9,闪现
当一个页面出错时跳转到另一个页面. 称之为闪现
关键语法
-设置:flash('aaa') -取值:get_flashed_message()
实例
fromflaskimportFlask,flash,get_flashed_messages,request,redirectapp=Flask(__name__)app.secret_key='asdfasdf'@app.route('/index')defindex():#从某个地方获取设置过的所有值,并清除。val=request.args.get('v')ifval=='oldboy':return'HelloWorld!'flash('超时错误',category="x1")return"ssdsdsdfsd"#returnredirect('/error')@app.route('/error')deferror():"""展示错误信息:return:如果get_flashed_messages(with_category=True)"""data=get_flashed_messages(category_filter=['x1'])ifdata:msg=data[0]else:msg="..."return"错误信息:%s"%(msg,)if__name__=='__main__':app.run()
10, 请求扩展
1 before_request
类比django中间件中的process_request,在请求收到之前绑定一个函数做一些事情
#基于它做用户登录认证 @app.before_request def process_request(*args,**kwargs): if request.path == '/login': return None user = session.get('user_info') if user: return None return redirect('/login')
2 after_request
类比django中间件中的process_response,每一个请求之后绑定一个函数,如果请求没有异常
@app.after_request def process_response1(response): print('process_response1 走了') return response
3 before-first-request
第一次请求与浏览器无关
@app.before_first_request def first(): pass
4 teardown_request
每一个请求之后绑定一个函数,即使遇到了异常
@app.teardown_request def ter(e): pass
5 errorhandler
路径不存在时404,服务器内部错误500
@app.errorhandler(404) def error_404(arg): return "404错误了"
6 template_global
标签
@app.template_global() def sb(a1, a2): return a1 + a2 #{{sb(1,2)}}
7 template_filter
过滤器
@app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 #{{ 1|db(2,3)}}
ps:
1 重点掌握before_request和after_request,
2 注意有多个的情况,执行顺序
3 before_request请求拦截后(也就是有return值),response所有都执行
11, 中间件
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!' # 模拟中间件 class Md(object): def __init__(self,old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): print('开始之前') ret = self.old_wsgi_app(environ, start_response) print('结束之后') return ret if __name__ == '__main__': #1我们发现当执行app.run方法的时候,最终执行run_simple,最后执行app(),也就是在执行app.__call__方法 #2 在__call__里面,执行的是self.wsgi_app().那我们希望在执行他本身的wsgi之前做点事情。 #3 所以我们先用Md类中__init__,保存之前的wsgi,然后我们用将app.wsgi转化成Md的对象。 #4 那执行新的的app.wsgi_app,就是执行Md的__call__方法。 #把原来的wsgi_app替换为自定义的, app.wsgi_app = Md(app.wsgi_app) app.run()
请求流程
ctx = self.request_context(environ) error = None try: try: ctx.push() #根据路径去执行视图函数,视图类 response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: #不管出不出异常,都会走这里 if self.should_ignore_error(error): error = None ctx.auto_pop(error)