文章目录
FlaskWeb工作流程
HTTP请求
-
请求报文
-
使用request 的属性获取请求URL
假设请求的URL 是http://127.0.0.1:5000/hello?name=Grey
属性 值 path /hello full_path /hello?name=gery host 127.0.0.1:5000 host_url http://127.0.0.1:5000/ base_url http://127.0.0.1:5000/hello url http://127.0.0.1:5000/hello?name=gery url_root http://127.0.0.1:5000/ -
request 对象常用的属性和方法
属性、方法 说明 args blueprint 当前蓝本名称 cookies 请求报文中cookies字典 data endpoint 与当前请求匹配的端点值 files 获取所有上传文件,使用字典形式获取. 调用save()方法传入路径保存文件 form 获取表单数据 values 结合了args和form属性的值 get_data(cache=True, as_text=False, parse_from_data=False) 获取消求中的数据, 默认读取为字节字符串( bytestring ), 将as 一text 设为True 返回值将是解码后的uni code 字符串 get_json() headers 返回首部字典,可以以字典形式操作 is_json 通过MIME 类型判断是否为JSON 数据,返回布尔值 json 包含解析后的json数据,内部调用get_json(),可通过字典方式获取键值 method 请求的HTTP方法 referrer 请求发起的源URL scheme 请求的URL模式(http或https) user_agent 用户代理,包含用户客户端类型、操作系统等信息 -
请求钩子
需要对请求进行预处理和后处理,可以使用flask的请求钩子.
钩子 说明 before_first_request 注册一个函数,在处理第一个请求前运行 before_request 注册一个函数,在处理每个请求前运行 after_request 注册一个函数,如果没有未处理的异常抛出,会在每个请求结束后运行 teardown_request 注册一个函数,即使有未处理的异常抛出,会在每个请求结束后运行. 如果发生异常,会传入异常对象作为参数到注册函数中. after_this_request 在视图函数内注册一个函数,会在这个请求结束后运行
HTTP响应
-
响应报文
响应报文由协议版本、状态码、原因短语、响应首部和响应主体组成. 以localhost:5000/hello请求为例:
-
常见的http状态码
-
生成响应
普通的响应只包含主体内容:
@app.route('/hello') def hello(): ... retuen '<h1>Hello, Flask!</h1>'
默认的状态码为200,下面指定了不同的状态码:
def hello(): ... retuen '<h1>Hello, Flask!</h1>', 201
重定向响应:默认情况下 redirect返回码为302.
@app.route('/redirect') def redirect_302(): return redirect('http://www.baidu.com', code=301) return redirect(url_for('greet')) # 重定向到视图函数内部请求
错误响应:
@app.route('/404') def abort_404(): abort(404) # abort之后的代码将不在执行
-
响应格式
在HTTP响应中,我们大多数情况使用HTML格式进行数据传输. 不同响应的数据格式需要设置不同的MIME类型,MIME类型在首部的Content-Type字段定义:
MIME类型(又成为media type或content type)是一种用来标识文件类型的机制,可以让客户端区分不同的内容格式,执行不同的操作. 一般格式为"类型名/子类型名",比如"text/html"、"image/png" 完整的MIME类型列表:https://www.iana.org/assignments/media-types/media-types.xhtml
如果想要使用其他的MIME类型,可以通过Flask提供的make_response()方法生成响应对象,传入响应主体作为参数,然后使用响应对象的mimetype属性设置MIME类型,比如:
@app.route('/foot') def foo(): response = make_response("Hello, world!") # 生成响应对象,参数为响应主体 response.mimetype = "text/plain" # 调用响应对象的mimetype属性进行设置MIME类型 纯文本 return response
常用的数据格式:
数据格式 说明 text/plain 纯文本 text/html HTML(超文本标记语言) application/xml XML(可扩展标记语言),XML中的标签用于定义数据,一般作为AJAX请求的响应格式或web api响应格式 application/json 较XML清量、简洁、容易阅读等优点 flask中使用jsonify函数对json数据格式进行序列化输出,形成响应:
@app.route('/foo') def foo(): return jsonify(name='Grey Li', gender='male') return jsonify({name: 'Grey Li', gender: 'male'}) return jsonify(message="Error"!), 500 以上两种形式返回值都是相同的,生成下面的JSON字符串: '{"name": "Grey Li", "gender": "male"}' 另外,jsonify()函数默认生成200响应码,可以附加状态码自定义响应码.
-
Response类的常用属性和方法
方法、属性 说明 headers 一个Werkzeug的Headers对象,表示响应首部,可以像字典一样操作 status 状态码,文本类型 status_code 状态码,整型 mimetype MIME类型(仅包含内容类型部分) set_cookie() 设置cookie
Cookie
HTTP是无状态协议. 对于一次请求、响应结束后,服务端不会记录任何请求相关信息.
但是对于某些Web程序,客户端的某些信息必须被记住,比如用户登陆状态,这样才可以根据用户的状态返回不同的响应.
Cookie技术通过在请求和响应报文中添加Cookie数据来保存客户端状态信息.
附注:Cookie指Web服务器为了存储某些数据(比如用户信息)而保存在浏览器上的小型文本. 浏览器会在一定时间内保存它,并在下一次向
同一个服务器发送请求时附带这些数据.
-
响应添加Cookie
在Flask中,一般使用Response类提供的set_cookie()方法. 要使用这个方法,我们需要先使用make_response()方法手动生成一个响应对象,传入响应主体作为参数. 这个响应对象默认实例化内置的Response类.
set_cookie()方法参数:
属性 说明 key cookie的键(名称) value cookie的值 max_age cookie被保存的时间数,单位为秒;默认在用户会话结束(即关闭浏览器)时过期 expires 具体的过期时间,一个datetime对象或Unix时间戳 path 限制cookie只在给定的路径可用,默认整个域名 domain 设置cookie可用的域名 secure 如果设置为True,只有通过HTTPS才可以使用 httponly 如果设置为True,禁止客户端JavaScript获取cookie 示例:
# 设置cookie @app.route('/set/<name>') def set_cookie(name): response = make_response(redirect(url_for('say_hello'))) response.set_cookie('name', name) return response
浏览器第一次请求,可以看到响应对象已经携带set_cookie字段了,path为/
浏览器第二次请求,请求对象携带cookie字段
Session: 安全的cookie
Cookie是不安全的,可以通过对Cookie内容进行修改获得对网站的权限,冒充别人账户. Flask提供session对象来将Cooke加密存储.
附注:在编程中,session指用户会话,即服务器和客户端/浏览器间建立的交互活动.
在flask中,session对象用来加密cookie. 默认情况下,他会把数据存储在浏览器上一个名为session的cookie里.
-
实现session步骤
a) 设置程序密钥
b) 模拟用户登陆app.secret_key = 'Lsjsjljslslj' OR 密钥写进.env文件或环境变量,使用os.getenv()获取 app.secret_key = os.getenv('SECRET_KEY', 'secret string')
示例只是简单的演示,实际登陆,我们需要提供登陆表单,验证账号和密码正确性. session可以像字典一样操作,我们向session中添加一个logged true,表示已经认证.
session是采用密钥加密发送到客户端的,因此session是不能被修改的,除非用户知道密钥.@app.route('/login') def login(): session['logged_in'] = True # 写入session return redirect(url_for('say_hello'))
Flask上下文
Flask有两种上下文:程序上下文和请求上下文.
-
上下文全局变量
Flask通过本地线程(thread local)技术将请求对象在特定的线程和请求中全局可访问.
Flask提供了四个上下文全局变量,访问上下文环境中的信息:
变量名 上下文类别 说明 current_app 程序上下文 指向处理请求的当前程序实例对象 g 程序上下文 替代python的全局变量用户,确保仅在当前请求可用. 用于存储全局数据,每次请求都会重设 request 请求上下文 封装客户端发出的请求报文数据 session 请求上下文 会话认证 -
current_app
既然有了程序实例app对象,为什么还需要current_app对象. 因为程序有可能会出现多个程序实例(app),为了获取对应的当前程序实例,而不是固定的某一个程序实例,需要使用current_app变量. -
g
因为g存储在程序上下文中,而程序上下文随着每一个请求进入而激活,随着每一个请求的处理完毕而销毁,每次请求都会重设这个值.
通常使用g结合请求钩子来保存每个请求处理前需要的全局变量,比如当前登入的用户对象、数据库连接等.
设置这个函数后,在其他视图函数中可以直接使用g.name来获取对应的值. g也支持类似字典的get()、pop()、一级setdefault()方法.@app.before_request def get_name(): g.name = request.args.get('name')
-
-
激活上下文
Flask会在下面情况激活程序上下文:
- 当我们使用flask run 命令启动程序时
- 使用旧的app.run()方法启动程序时
- 执行使用@app.cli.command()装饰器注册的flask 命令时
- 使用flask shell 命令启动Python Shell 时
当请求进入时,flask自动激活请求上下文,这是我们可以使用request、session变量. 另外,当请求上下文被激活时,程序上下文也会被激活. 当请求处理完毕后,请求上下文和程序上下文自动销毁. 因此,在请求处理阶段,两者拥有相同的生命周期.
提示:url_for()、jsonify()也依赖上下文,因此只能在视图函数使用. url_for()依赖请求上下文, jsonify()内部调用了current_app变量.
HTTP进阶
-
重定向到上一个页面
# 重定向回上一个页面 @app.route('/foo') def foo(): return '<h1>Foo Page.</h1><a href="%s">Do something and redirect</a>' % url_for('do_something', next=request.full_path) @app.route('/bar') def bar(): return '<h1>Bar Page.</h1><a href="%s">Do something and redirect</a>' % url_for('do_something', next="http://www.baiud.com") # 先找next,再找referrer,最后重定向到hello页面 def redirect_back(default='say_hello', **kwargs): for target in request.args.get('next'), request.referrer: # 如果target为真 继续往下走 if not target: continue if is_safe_url(target): return redirect(target) # 内部重定向写相对路径 return redirect(url_for(default, **kwargs)) # 验证是否存在next重定向 def is_safe_url(target): ref_url = urlparse(request.host_url) # 此处target=request.args.get('next') 也就是next跟的下一个页面 # 注意urljoin用法, 此处target会覆盖request.host_url,因为target=http://www.baidu.com # 要是一个相对路径的话,那么会进行url拼接 test_url = urlparse(urljoin(request.host_url, target)) return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc @app.route('/do_something') def do_something(): # 注意url_for用法 /hello?next=222 print(url_for('say_hello', next=222)) return redirect_back()
-
AJAX
-
认识AJAX
不重载页面的情况下和服务器进行数据交换. 加上JavaScript 和DOM(Document Object Model , 文档对象模型),我们就可以在接收到响应数据后局部更新页面.
-
JQuery发送AJAX请求
附注1:AJAX配置参数
附注2:JQuery和AJAX相关的方法
-
-
返回局部数据
处于处理AJAX请求的视图函数来说,我们不会返回完整的HTML响应,一般会返回局部数据,常见的三种类型如下:
-
纯文本或局部HTML模板
纯文本可以在JavaScript用来直接替换页面中的文本值,局部HTML则可以直接插入到页面中,比如返回评论列表:
@app.route('/comments/<int:user_id>') def get_comments(post_id): ... return render_template('comments.html')
-
JSON数据
JSON数据可以直接在JavaScript中直接操作:
@app.route('/profile/<int:user_id>') def get_profile(user_id): ... return jsonify(username=username, bio=bio)
在JQuery中的ajax()方法的回调中,响应主体中的JSON字符串会被解析为JSON对象,我们可以直接获取并进行操作.
-
空值
有些时候,程序中某些接受AJAX请求的视图并不需要返回数据给客户端,比如用来删除文章的视图. 这时我们可以直接返回空值,并将状态码指定为204(表示无内容),比如:
@app.route('/post/delete/<int:post_id>', methods=['DELETE']) def delete_post(post_id): ... return '', 204
-