获取请求中得查询字符串
from flask import Flask, request
app = Flask(__name__)
@app.route('/hello')
def hello():
name = request.args.get("name", "Flask") # 获取查询参数name得值,如果没有则使用后面得默认值 Flask
return "<h1>Hello, %s!" % name
查看程序中定义得所有路由
$ flask routes
设置监听得HTTP方法
@app.route("/hello", methods= ['GET','POST'])
def hello():
return "<h1>Hello, Flask!</h1>"
若 是 get,post请求之外得请求,flask返回 405错误(Method Not Allowed)
Flask内置得URL变量转换器
@app.route('goback/<int:year>')
def go_back(year):
return "<p>Welcome to %d!</p>" % (2018 - year)
@app.route("/colors/<any(blur, white, red):color>")
def three_colors(color):
return '<p>Love is patient and kind. Love is not jealous or boastful or proud or rude.</p>'
传入一个预先定义得列表
colors = ['blue','white','red']
@app.route('/colors/<any(%s):color>' % str(colors)[1:-1])
请求钩子
before_first_request:注册一个函数,在处理第一个请求前运行before_request:注册一个函数吗,在处理每个请求前运行after_request:注册一个函数,如果没有未处理得异常抛出,会在每个请求结束后运行teardown_request:注册一个函数,即使有未处理得异常退出,会在每个请求结束后运行。如果发生异常,会传入异常对象作为参数到注册得函数中after_this_request:在视图函数内注册一个函数,会在这个请求结束后运行
响应
Flask会先判断是否可以找到与请求URL相匹配得路由,如果么有则返回404响应。如果找到,则调用对应得视图函数,视图函数得返回值构成了响应报文得主体内容,正确返回时状态码默认为200、Flask会调用make_response()方法将视图函数返回值转换为响应对象。
普通响应
@app.route('/hello')
def hello():
...
return "Hello Flask"
含有状态码得响应
@app.route('/hello')
def hello():
...
return "Hello Flask" ,201
重定向响应
场景:当某个用户在没经过认证得情况下访问需要登录后才能访问得资源
生成302状态码得重定向响应,并且将首部中的Location字段设置成重定向目标URL:
@app.route('/hello')
def hello():
...
return "", 302, {'Location':"http://www.example.com"}
from flask import Flask, redirect
@app.route('/hello')
def hello():
return redirect('http://www.example.com')
return redirect('http://www.example.com',code=303) # code默认为302重定向得状态码,可以自定义
手动返回错误响应
abort()
from flask import Flask, abort
@app.route('/404')
def not_found():
abort(404)
响应格式得查看
from flask import make_response
@app.route('/foo')
def foo():
response = make_response("hello world!")
response.mimetype = "text/plain"
return response
responses.headers['Content-Type'] = "text/xml; charset=utf-8"
Json得用法
from flask import Flask, make_response, json
@app.route('/foo')
def foo():
data = {
"name":"Grey li",
"gender":"male"
}
response = make_response(json.dumps(data))
response.mimetype = 'application/json'
return response
from flask import jsonify
@app.route('/foo')
def foo():
return jsonify(name='Grey li', gender = 'male')
return jsonify({name: 'Grey Li', gender: 'male'})
jsonify() 默认生成200得响应,可以自定义
jsonify(messages = 'Error!'), 501
Cookie
在Flask中,要是想在响应中添加一个cookie,最方便的方法就是在Response类提供的set_cookie()方法。我们需要先使用make_response()方法手动生成一个响应对象,传入响应主体作为参数。这个响应对象默认实例化内置的Response类。
from flask import Flask, make_response
@app.route('/set/<name>')
def set_cookie(name):
response = make_response(redirect(url_for('hello')))
response.set_cookie('name', name)
return response
当浏览器保存了服务器端设置的cookie后,浏览器再次发送到该服务器的请求会自动携带设置的Cookie信息,Cookie的内容储存在请求首部的Cookie字段中。
读取cookie
from flask import Flask, request
@app.route("/")
@app.route("/hello")
def hello():
name = request.args.get("name")
if name is None:
name = request.cookies.gey("name","Human") # 如果cookie中没有,则使用Human
return "<h1>Hello, %s!</h1>" % name
Session:安全的Cookie
在编程中,session指用户会话(user session),又称对话(dialogue),即服务器和客户端/浏览器之间或桌面程序和用户之间建立得交互行动。
- 设置程序密钥
app.secret_key = 'secret string'
或者在 系统环境变量里面添加、`.env`文件中添加
SECRET_KEY=secret string
在程序脚本中用os模块提供的`getenv()`方法获取
import os
...
app.secret_key = os.getenv('SECRET_KEY', 'secret string') # 第二个参数为 没有获取到环境变量时使用得默认值
- 模拟用户认证
from flask import Flask, redirect, session, url_for
@app.route('/login')
session['logged_in'] = True # 写入session
return redirect(url_for('hello'))
from flask import Flask, redirect, session, url_for, request
app = Flask(__name__)
app.secret_key = 'secret string' # 加一个session可以读取得密钥
@app.route('/')
@app.route('/hello')
def hello():
return "Hello"
@app.route('/login')
def login():
session['logged_in'] = True # 写入session
return redirect(url_for('hello'))
- 按照用户的登录状态显示不同的内容
app.secret_key = 'secret string'
@app.route('/')
@app.route('/hello')
def hello():
name = request.args.get('name')
if name is None:
name = request.cookies.get('name', "Human")
response = "<h1>Hello, %s </h1>" % name
if 'logged_in' in session:
response += '[Auth]'
else:
response += '[unAuth]'
return response
- 模拟管理后台
from flask import abort
@app.route('/admin')
def admin():
if "logged_in" not in session:
abort(403) # 手动抛出异常
return 'Welcome to admin page.'
- 登出用户
@app.route('/logout')
def logout():
if 'logged_in' in session:
session.pop('logged_in')
return redirect(url_for('hello'))
session cookie 会在用户关闭浏览器时删除 通过
session.permanent属性设为True可以将session得有效期延长为Flask.permanent.session_lifetime属性对应得datetime.timedelta对象,也可以通过配置变量PERMANENT_SESSION_LIFETIME设置,默认为31天。
Flask上下文
Version:0.9 StartHTML:0000000105 EndHTML:0000001293 StartFragment:0000000141 EndFragment:0000001253我们可以把编程中的上下文理解为当前环境(environment)的快照(snapshot)。如果把一个Flask程序比作一条可怜的生活在鱼缸里的鱼的话,那么它当然离不开身边的环境。
- 程序上下文(application context)
- 请求上下文 (request context)
current_app: 程序上下文:指向处理请求得当前程序实例g:替代python得全局变量用法,确保仅在当前请求中可用,用于储存全局数据,每次请求都会重设request:封装客户端发出得请求报文数据session:用于记住请求之间的数据,通过签名得Cookie实现
g 得实现
from flask import g
@app.before_request
def get_name():
g.name = request.args.get('name')
激活上下文
- 使用 flask run命令启动程序
- 使用 app.run()启动程序
- 使用@app.cli.command()装饰器注册得flaks命令时
- 使用flask shell 启动python shell时
当请求进入时,Flask会自动激活请求上下文,这时我们可以使用request和session变量。另外,当请求上下文被激活时,程序上下文也被自动激活。当请求处理完毕后,请求上下文和程序上下文也会自动销毁。也就是说,在请求处理时这两者拥有相同的生命周期。
手动激活上下文
- 使用
app.app_context()
from app import app
from flask import current_app
with app.app_context():
current_app.name
----
'app'
- 显式使用 push()方法推送(激活)上下文,执行完之后 使用 pop()销毁上下文
from app import app
from flask import current_app
app_ctx = app.app_context()
app_ctx.push()
current_app.name
----
'app'
---
app_ctx.pop()
- 通过
test_request_context()方法临时创建
from app import app
from flask import request
with app.test_request_context('/hello'):
request.method
----
"GET"
上下文钩子
teardown_appcontext 钩子,使用它注册的回调函数会在程序上下文被销毁时调用。
- re:需要在每个请求处理结束后销毁数据库连接
@app.teardown_appcontext
def teardown_db(exception):
...
db.close()
使用app.teardown_appcontext装饰器注册的回调函数需要接收异常对象作为参数,当请求被正常处理时这个参数值将是None,这个函数的返回值将被忽略。
重定向回前一个页面
场景:用户登录完成后,返回得是登录前得页面,而不是从首页开始。
↓:这种是使用固定链接来每次返回主页,但是我们并不想这么做,所以需要另想办法:
from flask import Flask, redirect, session, url_for, request
app = Flask(__name__)
@app.route('/hello')
def hello():
return "Welcome!"
@app.route('/foo')
def foo():
return "<h1>Foo page</h1><a href='%s'> Do Something</a>" % url_for('do_something') # 点击链接后返回 do_something函数调用hello页面方法
@app.route('/bar')
def bar():
return "<h1>Bar page</h1><a href='%s'> Do Something</a>" % url_for('do_something')
@app.route('/do_something')
def do_something():
return redirect(url_for('hello'))
我们可以使用得方式如下:
- 获取上一个页面得URL
- HTTP referer
request.referrer(没打错 就是这么写的,这样是正确的调用函数拼写方式)
- HTTP referer
return redirect(request.referrer)
# 因为不可避免得因素(防火墙软件,浏览器设置关闭referrer字段),所以需要添加一个备选得地址,以免报错
return redirect(request.referrer or url_for('/hello'))
- 查询函数
-
在参数中加
next:包含当前页面URLnext=request.full_path→ 获取完整得路径
-
from flask import Flask, redirect, session, url_for, request
app = Flask(__name__)
@app.route('/hello')
def hello():
return "Welcome!"
@app.route('/foo')
def foo():
return "<h1>Foo page</h1><a href='%s'> Do something</a>" % url_for('do_something', \
next=request.full_path)
@app.route('/bar')
def bar():
return "<h1>Bar page</h1><a href='%s'> Do something</a>" % url_for('do_something', \
next=request.full_path) # 加入这个参数 是获取当前页面得完整路径,之后好返回
@app.route('/do_something')
def do_something():
return redirect(request.args.get("next", url_for('hello'))) # 获取值得时候,如果没有获取到 则使用第二个参数
1. 优化参数,使得 获取上一个页面得url和查询函数融合到一起
from flask import Flask, redirect, session, url_for, request
app = Flask(__name__)
@app.route('/hello')
def hello():
return "Welcome!"
@app.route('/foo')
def foo():
return "<h1>Foo page</h1><a href='%s'> Do something</a>" % url_for('do_something', next=request.full_path)
@app.route('/bar')
def bar():
return "<h1>Bar page</h1><a href='%s'> Do something</a>" % url_for('do_something', next=request.full_path)
=========================================================================
def redirect_back(default='helo', **kwargs):
for target in request.args.get('next'), request.referrer:
if target:
return redirect(target)
return redirect(url_for(default, **kwargs))
@app.route('/do_something_and_redirect')
def do_something():
# return redirect(request.args.get("next", url_for('hello')))
return redirect_back()
对URL进行安全认证
因为如果是只用next()来拼接得话,那么被人窃取得几率或者是被人篡改得几率就比较大。
from urllib.parse import urlparse, urljoin
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
def redirect_back(default='helo', **kwargs):
for target in request.args.get('next'), request.referrer:
if not target:
continue
if is_safe_url(target):
return redirect(target)
return redirect(url_for(default, **kwargs))
使用Ajax技术
AJAX指异步Javascript和XML(Asynchronous JavaScript AndXML),它不是编程语言或通信协议,而是一系列技术的组合体。简单来说,AJAX基于XMLHttpRequest(xhr.spec.whatwg.org/)让我们可以在不重载页面的情况下和服务器进行数据交换。加上JavaScript和DOM(Document Object Model,文档对象模型),我们就可以在接收到响应数据后局部更新页面。而XML指的则是数据的交互格式,也可以是纯文本(Plain Text)、HTML或JSON。顺便说一句,XMLHttpRequest不仅支持HTTP协议,还支持FILE和FTP协议。
使用Ajax技术时得流程如下:
- 当单击 删除 按钮时,客户端在后台发送一个异步请求,页面不变,在接收响应前可以进行其他操作
- 服务器端接收请求后执行删除操作,返回提示消息或是无内容得204响应。
- 客户端接收到得响应,使用JavaScript更新页面,移除资源对应得页面元素。
使用全局JQuery函数ajax()发送Ajax请求
返回局部数据
- 纯文本或局部HTML模板
@app.route("/comments/<int:post_id>")
def get_comments(post_id):
...
return render_template('comments.html')
- Json数据
@app.route("/profile/<int:user_id>")
def get_profile(post_id):
...
return jsonify(username=username, bio=bio)
- 空值
@app.route("/post/delete/<int:post_id>", methods=['DELETE'])
def delete_post(post_id):
...
return '', 204
- 异步加载长文章
from jinja2.utils import generate_lorem_ipsum
from flask import Flask
app = Flask(__name__)
@app.route('/post')
def show_post():
post_body = generate_lorem_ipsum(n=2) # 生成两段随机文本
return '''
<h1>A very long post</h1>
<div class="body">%s</div>
<button id="load">Load More</button>
<script src="https://code.jquery.com/jquery-3.3.1.min.js">
</script>
<script type="text/javascript">
$(function() {
$('#load').click(function() {
$.ajax({
url: '/more', // 目标URL
type: 'get', // 请求方法
success: function(data){ // 返回2XX响应后触发的回调函数
$('.body').append(data); // 将返回的响应插入到页面中
}
})
})
})
</script>''' % post_body
@app.route("/more")
def load_post():
return generate_lorem_ipsum(n=1) # 生成一段随机文本
HTTP服务端推送(server push)
在某些场景下,我们需要的通信模式是服务器端的主动推送(server push)。比如,一个聊天室有很多个用户,当某个用户发送消息后,服务器接收到这个请求,然后把消息推送给聊天室的所有用户。类似这种关注实时性的情况还有很多,比如社交网站在导航栏时显示新提醒和私信的数量,用户的在线状态更新,股价行情监控、显示商品库存信息、多人游戏、文档协作等。
如何有效放置 代码得稳定性
- sql注入
- xss攻击
- csrf攻击