本文将分享pythonweb框架Flask——csrf攻击的详细内容,并且还将对phpcsrf攻击进行详尽解释,此外,我们还将为大家带来关于flask——CSRFToken保护、pthonweb框架f
本文将分享python web框架Flask——csrf攻击的详细内容,并且还将对php csrf攻击进行详尽解释,此外,我们还将为大家带来关于flask——CSRFToken保护、pthon web框架flask(二)--快速入门、Python Flask Web框架教程 2 模板、Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架的相关知识,希望对你有所帮助。
本文目录一览:- python web框架Flask——csrf攻击(php csrf攻击)
- flask——CSRFToken保护
- pthon web框架flask(二)--快速入门
- Python Flask Web框架教程 2 模板
- Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架
python web框架Flask——csrf攻击(php csrf攻击)
CSRF是什么?
(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用也就是人们所知道的钓鱼网站。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
CSRF可以做什么?
你这可以这么理解 CSRF
攻击:攻击者盗用了你的身份,伪装成你发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。
它这么厉害,那它的原理是什么?
CSRF原理简述
用户访问一个网站就会将用户的相关信息保存cookies中(session可以认为加了密的cookies,然后保存到的cookies中),这时用户又访问了一个很危险的网站,这个网站就会利用你之前访问网站留下的cookies发送一些恶意请求
CSRF预防的方式
我总结了两种预防CSRF攻击的方式:后台直接处理CSRF攻击(个人说法)和前端ajax请求方式
后台处理CSRF攻击
很简单,只需要在表单中增加一个name属性值为csrf_token、value属性值为{{ csrf_token() }} 的隐藏的input表单标签。
<form class="form-signin" method="post">
<h2 class="form-signin-heading">请登录</h2>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label for="inputEmail" class="sr-only">邮箱:</label>
<input type="email" id="inputEmail" class="form-control" name="email" placeholder="请输入邮箱地址" required autofocus>
<label for="inputPassword" class="sr-only">密码:</label>
<input type="password" id="inputPassword" class="form-control" name="password" placeholder="请输入密码" required>
<div class="checkbox">
<label>
<input type="checkbox" name="remember" value="1"> 记住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">立即登录</button>
</form>
后台的csrftoken需要绑定到app上,这样前端表单传入的参数不会受到CSRF攻击就可以像平常一样获取post参数即可(上篇python web框架Flask后台登录,就是使用这种方式处理CSRF攻击的)
def create_app():
"""
主入口文件创建app,供其他蓝图使用
:return: 返回一个app
"""
app = Flask(__name__)
# 防止csrf注入攻击
CSRFProtect(app)
# 注册蓝图模块
app.register_blueprint(cms_bp, url_prefix="/cms")
app.register_blueprint(common_bp, url_prefix="/common")
app.register_blueprint(front_bp)
# 导入配置文件
app.config.from_object(config)
# 数据库db初始化app
db.init_app(app)
# 后台登录login_manager初始化app
cms_login_manager.init_app(app)
return app
if __name__ == ''__main__'':
app = create_app()
app.run()
前端ajax请求方式
前端Ajax请求是实现页面刷新的一种非常好的方式,但是Ajax请求也会有CSRF攻击。防止CSRF攻击也很简单,只需要两步:
1)在当前页面上添加一个name为csrf-token, content为{{csrf_token()}}的meta标签
<meta name="csrf-token" content="{{ csrf_token() }}">
2)重写Ajax请求,并返回一个含有csrftoken的请求头
''use strict'';
var cpajax = {
"get": function(args){
args["method"] = "get";
this.ajax(args);
},
"post": function(args){
args["method"] = "post";
this.ajax(args);
},
"ajax": function(args){
this._ajaxSetup();
$.ajax(args);
},
"_ajaxSetup": function(args){
$.ajaxSetup({
"beforeSend": function(xhr, settings){
if(!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain){
var csrftoken = $("meta[name=csrf-token]").attr("content");
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
}
};
3)这样前端就不能使用$.post()请求了(这种请求不能阻止CSRF攻击的),而是使用我们重写的Ajax请求。
$(function(){
$("#submit").on("click", function(event){
event.preventDefault();
var oldpwdE = $("input[name=oldpwd]");
var newpwdE = $("input[name=newpwd]");
var newpwd2E = $("input[name=newpwd2]");
var oldpwd = oldpwdE.val();
var newpwd = newpwdE.val();
var newpwd2 = newpwd2E.val();
//1、要在模板中的meta标签中渲染一个csrf-token
//2、在ajax请求的头部设置x-CSRFtoken
console.log("aaaaaaa");
cpajax.post({
"url": "/cms/resetpwd",
"data": {
"oldpwd": oldpwd,
"newpwd": newpwd,
"newpwd2": newpwd2
},
"success": function(data){
console.log(data)
},
"fail": function(error){
console.log(error)
}
})
})
});
flask——CSRFToken保护
flask总结05(在 Flask 项目中解决 CSRF 攻击)
一:安装 flask_wtf
pip install flask_wtf
二:设置应用程序的 secret_key,用于加密生成的 csrf_token 的值
# session加密的时候已经配置过了.如果没有在配置项中设置,则如下:
app.secret_key = "#此处可以写随机字符串#"
三:导入 flask_wtf.csrf 中的 CSRFProtect 类,进行初始化,并在初始化的时候关联 app
from flask.ext.wtf import CSRFProtect
CSRFProtect(app)
四:在表单中使用 CSRF 令牌:
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
五:scrf的过程理解
代码显示方法不被允许的代码
#manage.py
from flask import Flask, render_template, request, g
from settings.dev import DevConfig
from flask.ext.wtf import CSRFProtect
app = Flask(__name__, template_folder="templates", static_folder="static")
app.config.from_object(DevConfig)
CSRFProtect(app)
# @app.route("/csrf_test", methods=["get", "post"])
@app.route("/csrf_test")
def index():
if request.method == "GET":
return render_template("form.html")
else:
print(request.form)
return "ok"
if __name__ == "__main__":
app.run()
templates下的form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>csrf案例</title>
</head>
<body>
<form action="" method="post">
账号:<input type="text" name="username"><br><br>
密码:<input type="password" name="password"><br><br>
<input type="submit" value="提交">
</form>
</body>
</html>
运行后显示的结果是:
然后修改manage.py中的代码,添加
@app.route("/csrf_test", methods=["get", "post"])
代码如下:
from flask import Flask, render_template, request, g
from settings.dev import DevConfig
from flask.ext.wtf import CSRFProtect
app = Flask(__name__, template_folder="templates", static_folder="static")
app.config.from_object(DevConfig)
CSRFProtect(app)
@app.route("/csrf_test", methods=["get", "post"])
def index():
if request.method == "GET":
return render_template("form.html")
else:
print(request.form)
return "ok"
if __name__ == "__main__":
app.run()
再次执行:并且提交:
说明需要在html文档中添加:csrf_token
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
修改后的代码是:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>csrf案例</title>
</head>
<body>
<form action="" method="post">
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
账号:<input type="text" name="username"><br><br>
密码:<input type="password" name="password"><br><br>
<input type="submit" value="提交">
</form>
</body>
</html>
运行后的结果显示:
flask——CSRFToken保护
根据 csrf_token 校验原理,具体操作步骤有以下几步:
1.后端生成 csrf_token 的值,在前端请求登录或者注册界面的时候将值传给前端,传给前端的方式可能有以下两种:
在模板中的 From 表单中添加隐藏字段
将 csrf_token 使用 cookie 的方式传给前端
2.在前端发起请求时,在表单或者在请求头中带上指定的 csrf_token
3.后端在接受到请求之后,取到前端发送过来的 csrf_token,与第1步生成的 csrf_token 的值进行校验
4.如果校验对 csrf_token 一致,则代表是正常的请求,否则可能是伪造请求,不予通过
而在 Flask 中,CSRFProtect 这个类专门只对指定 app 进行 csrf_token 校验操作,所以开发者需要做以下几件事情:
生成 csrf_token 的值
将 csrf_token 的值传给前端浏览器
在前端请求时带上 csrf_token 值
生成 csrf_token 的值
# 导入生成 csrf_token 值的函数
from flask_wtf.csrf import generate_csrf
# 调用函数生成 csrf_token
csrf_token = generate_csrf()
将 csrf_token 的值传给前端浏览器
实现思路:可以在请求勾子函数中完成此逻辑
@app.after_request
def after_request(response):
# 调用函数生成 csrf_token
csrf_token = generate_csrf()
# 通过 cookie 将值传给前端
response.set_cookie("csrf_token", csrf_token)
return response
在前端请求时带上 csrf_token 值
根据登录和注册的业务逻辑,当前采用的是 ajax 请求
所以在提交登录或者注册请求时,需要在请求头中添加 X-CSRFToken 的键值对
$.ajax({
url:"/passport/register",
type: "post",
headers: {
"X-CSRFToken": getCookie("csrf_token")#get_Cookie为自定义函数
},
data: JSON.stringify(params),
contentType: "application/json",
success: function (resp) {
if (resp.errno == "0"){
// 刷新当前界面
location.reload()
}else {
$("#register-password-err").html(resp.errmsg)
$("#register-password-err").show()
}
}
})
flask csrf保护的使用技巧
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。 ——百度百科
网上关于csrf攻击的一个最典型的例子是银行通过url+参数转钱的问题,很好理解。
flask 作为一个强带的web微框架,自然也是支持防范csrf攻击的。
通过Flask-WTF来保护表单免受CSRF攻击
何为Flask-WTF
简单说来,使用它可以方便我们构建表单和验证表单,具体用法这里不做赘述
怎么开启保护?
极少的配置,一个应用令牌:
app.config[''SECRET_KEY''] = ''you can''t know''
或csrf专用令牌
app.config[''WTF_CSRF_SECRET_KEY''] = ''IT 666 to 709 kill pic''
Flask-WTF默认是开启CSRF保护的,你需要的做的是在你的模板中表单一栏加入:
{{ form.csrf_token }}
或者
{{ form.hidden_tag() }}
这样,csrf保护已经开启。如若你没有上面的代码,提交表单会无法通过form.validate_on_submit()
,错误为
{''csrf_token'': [''The CSRF token is missing.'']},但这并不会导致http错误,仅是无法通过验证。
关闭保护
自然,我们很可能有的表单不需要保护,那么,以下三种:
全局禁用:app.config中设置WTF_CSRF_ENABLED = False
单个表单禁用:生成表单时加入参数form = Form(csrf_enabled=False)
不使用Flask-WTF…啊啊,这个很好理解
http://docs.jinkan.org/docs/flask-wtf/from.html
flask 内置的保护机制
有些时候,我根本就用不到Flask-WTF,那么我可以使用flask自身提供的保护机制。
开启保护
要开启csrf攻击保护,需要一下几个步骤。
from flask_wtf.csrf import CSRFProtect
app.config[''SECRET_KEY''] = ''you never guess''
CSRFProtect(app)
主要为:导入方法,设置密钥,进行保护。
这时候对于一个普通的form,如果你没有
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
flask会抛出http错误。
临时关闭保护
那么,如何临时关闭csrf保护呢。
上面的代码做修改:
去掉 CSRFProtect(app)
改为 csrf = CSRFProtect()
if __name__ == ''__main__'':中加入
csrf.init_app(app)
在不需要保护的路由上当加上:
@csrf.exempt
如此,临时关闭了csrf。
http://docs.jinkan.org/docs/flask-wtf/csrf.html
pthon web框架flask(二)--快速入门
快速入门
迫切希望上手?本文提供了一个很好的 Flask 介绍。假设你已经安装 Flask, 如果还没有安装话,请浏览下 安装 。
一个最小的应用
一个最小的应用看起来像这样:
1 from flask import Flask
2 app = Flask(__name__)
3
4 @app.route(''/'')
5 def hello_world():
6 return ''Hello World!''
7
8 if __name__ == ''__main__'':
9 app.run()
把它保存成 hello.py (或者类似的文件),然后用 Python 解释器运行它。确保你的应用不叫做 flask.py, 因为这会与 Flask 本身冲突。
$ python hello.py
* Running on http://127.0.0.1:5000/
现在浏览 http://127.0.0.1:5000/,你会看到你的 Hello World 问候。
那么这段代码做了什么?
- 首先我们导入了类
Flask
。这个类的实例化将会是我们的 WSGI 应用。第一个参数是应用模块的名称。 如果你使用的是单一的模块(就如本例),第一个参数应该使用 __name__。因为取决于如果它以单独应用启动或作为模块导入, 名称将会不同 (''__main__''
对应于实际导入的名称)。获取更多的信息,请阅读Flask
的文档。 - 接着,我们创建一个该类的实例。我们传递给它模块或包的名称。这样 Flask 才会知道去哪里寻找模板、静态文件等等。
- 我们使用装饰器
route()
告诉 Flask 哪个 URL 才能触发我们的函数。 - 定义一个函数,该函数名也是用来给特定函数生成 URLs,并且返回我们想要显示在用户浏览器上的信息。
- 最后我们用函数
run()
启动本地服务器来运行我们的应用。if __name__ ==''__main__'':
确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。
请按 control-C 来停止服务器。
外部可见服务器
当你运行服务器,你会注意到它只能从你自己的计算机上访问,网络中其它任何的地方都不能访问。 这是因为默认情况下,调试模式,应用中的一个用户可以执行你计算机上的任意 Python 代码。
如果你关闭 debug 或者信任你所在网络上的用户,你可以让你的服务器对外可用,只要简单地改变方法 run()
的调用像如下这样:
app.run(host=''0.0.0.0'')
这让你的操作系统去监听所有公开的 IP。
调试模式
run()
方法是十分适用于启动一个本地开发服务器,但是你需要在修改代码后手动重启服务器。 这样做并不好,Flask 能做得更好。如果启用了调试支持,在代码修改的时候服务器能够自动加载, 并且如果发生错误,它会提供一个有用的调试器。
有两种方式开启调式模式。一种是在应用对象上设置标志位:
app.debug = True app.run()
或者作为 run 的一个参数传入:
app.run(debug=True)
两种方法效果是一样的。
Attention
尽管交互式调试器不能在分叉( forking )环境上工作(这使得它几乎不可能在生产服务器上使用), 它依然允许执行任意代码。这使它成为一个巨大的安全风险,因此它 绝对不能用于生成环境。
运行中的调试器的截图:

是不是还有其它的调试器?请查看 使用调试器。
路由
现代 Web 应用程序有优雅的 URLs。这能够帮助人们记住 URLs,这点在面向使用慢网络连接的移动设备的应用上有用。 如果用户不必通过点击首页而直接访问想要的页面,很可能他们会喜欢这个页面而且下次再次访问。
正如上面所说, route()
装饰器是用于把一个函数绑定到一个 URL 上。这有些基本的例子:
@app.route(''/'')
def index(): return ''Index Page'' @app.route(''/hello'') def hello(): return ''Hello World''
但是不仅如此!你可以动态地构造 URL 的特定部分,也可以在一个函数上附加多个规则。
变量规则
为了给 URL 增加变量的部分,你需要把一些特定的字段标记成 <variable_name>
。这些特定的字段将作为参数传入到你的函数中。当然也可以指定一个可选的转换器通过规则 <converter:variable_name>
。 这里有一些不错的例子:
@app.route(''/user/<username>'')
def show_user_profile(username): # show the user profile for that user return ''User %s'' % username @app.route(''/post/<int:post_id>'') def show_post(post_id): # show the post with the given id, the id is an integer return ''Post %d'' % post_id
存在如下转换器:
int | 接受整数 |
float | 同 int 一样,但是接受浮点数 |
path | 和默认的相似,但也接受斜线 |
唯一 URLs / 重定向行为
Flask 的 URL 规则是基于 Werkzeug 的 routing 模块。 该模块背后的想法是基于 Apache 和早期的 HTTP 服务器定下先例确保优雅和唯一的 URL。
以这两个规则为例:
@app.route(''/projects/'')
def projects(): return ''The project page'' @app.route(''/about'') def about(): return ''The about page''
虽然它们看起来确实相似,但它们结尾斜线的使用在 URL 定义 中不同。 第一种情况中,规范的 URL 指向 projects 尾端有一个斜线。 这种感觉很像在文件系统中的文件夹。访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范URL去。
然而,第二种情况的 URL 结尾不带斜线,类似 UNIX-like 系统下的文件的路径名。 访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。
当用户访问页面时忘记结尾斜线时,这个行为允许关联的 URL 继续工作, 并且与 Apache 和其它的服务器的行为一致。另外,URL 会保持唯一,有助于避免搜索引擎索引同一个页面两次。
构建 URL
如果它可以匹配 URL,那么 Flask 能够生成它们吗?当然 Flask 能够做到。你可以使用函数 url_for()
来针对一个特定的函数构建一个 URL。它能够接受函数名作为第一参数,以及一些关键字参数, 每一个关键字参数对应于 URL 规则的变量部分。未知变量部分被插入到 URL 中作为查询参数。这里有些例子:
>>> from flask import Flask, url_for >>> app = Flask(__name__) >>> @app.route(''/'') ... def index(): pass ... >>> @app.route(''/login'') ... def login(): pass ... >>> @app.route(''/user/<username>'') ... def profile(username): pass ... >>> with app.test_request_context(): ... print url_for(''index'') ... print url_for(''login'') ... print url_for(''login'', next=''/'') ... print url_for(''profile'', username=''John Doe'') ... / /login /login?next=/ /user/John%20Doe
(这里也使用了 test_request_context()
方法,下面会给出解释。这个方法告诉 Flask 表现得像是在处理一个请求,即使我们正在通过 Python 的 shell 交互。 请看下面的解释。 局部上下文)。
为什么你愿意构建 URLs 而不是在模版中硬编码?这里有三个好的理由:
- 反向构建通常比硬编码更具备描述性。更重要的是,它允许你一次性修改 URL, 而不是到处找 URL 修改。
- 构建 URL 能够显式地处理特殊字符和 Unicode 转义,因此你不必去处理这些。
- 如果你的应用不在 URL 根目录下(比如,在
/myapplication
而不在/
),url_for()
将会适当地替你处理好。
HTTP方法
HTTP (也就说 web 应用协议)有不同的方法来访问 URLs。默认情况下,路由只会响应 GET 请求, 但是能够通过给 route()
装饰器提供 methods 参数来改变。这里是些例子:
@app.route(''/login'', methods=[''GET'', ''POST'']) def login(): if request.method == ''POST'': do_the_login() else: show_the_login_form()
如果使用 GET 方法,HEAD 方法将会自动添加进来。你不必处理它们。也能确保 HEAD请求 会按照 HTTP RFC (文档在 HTTP 协议里面描述) 要求来处理, 因此你完全可以忽略这部分 HTTP 规范。 同样地,自从 Flask 0.6 后,OPTIONS 也能自动为你处理。
也许你并不清楚 HTTP 方法是什么?别担心,这里有一个 HTTP 方法的快速入门以及为什么它们重要:
HTTP 方法(通常也称为“谓词”)告诉服务器客户端想要对请求的页面 做 什么。下面这些方法是比较常见的:
- GET
- 浏览器通知服务器只 获取 页面上的信息并且发送回来。这可能是最常用的方法。
- HEAD
- 浏览器告诉服务器获取信息,但是只对 头信息 感兴趣,不需要整个页面的内容。 应用应该处理起来像接收到一个 GET 请求但是不传递实际内容。在 Flask 中你完全不需要处理它, 底层的 Werkzeug 库会为你处理的。
- POST
- 浏览器通知服务器它要在 URL 上 提交 一些信息,服务器必须保证数据被存储且只存储一次。 这是 HTML 表单通常发送数据到服务器的方法。
- PUT
- 同 POST 类似,但是服务器可能触发了多次存储过程,多次覆盖掉旧值。现在你就会问这有什么用, 有许多理由需要如此去做。考虑下在传输过程中连接丢失:在这种情况下浏览器 和服务器之间的系统可能安全地第二次接收请求,而不破坏其它东西。对于 POST 是不可能实现的,因为 它只会被触发一次。
- DELETE
- 移除给定位置的信息。
- OPTIONS
- 给客户端提供一个快速的途径来指出这个 URL 支持哪些 HTTP 方法。从 Flask 0.6 开始,自动实现了它。
现在比较有兴趣的是在 HTML4 和 XHTML1,表单只能以 GET 和 POST 方法来提交到服务器。在 JavaScript 和以后的 HTML 标准中也能使用其它的方法。同时,HTTP 最近变得十分流行,浏览器不再是唯一使用 HTTP 的客户端。比如,许多版本控制系统使用 HTTP。
静态文件
动态的 web 应用同样需要静态文件。CSS 和 JavaScript 文件通常来源于此。理想情况下, 你的 web 服务器已经配置好为它们服务,然而在开发过程中 Flask 能够做到。 只要在你的包中或模块旁边创建一个名为 static 的文件夹,在应用中使用 /static 即可访问。
给静态文件生成 URL ,使用特殊的 ''static''
端点名:
url_for(''static'', filename=''style.css'')
这个文件应该存储在文件系统上称为 static/style.css
。
渲染模板
在 Python 中生成 HTML 并不好玩,实际上是相当繁琐的,因为你必须自行做好 HTML 转义以保持应用程序的安全。 由于这个原因,Flask 自动为你配置好 Jinja2 模版。
你可以使用方法 render_template()
来渲染模版。所有你需要做的就是提供模版的名称以及你想要作为关键字参数传入模板的变量。这里有个渲染模版的简单例子:
from flask import render_template
@app.route(''/hello/'') @app.route(''/hello/<name>'') def hello(name=None): return render_template(''hello.html'', name=name)
Flask 将会在 templates 文件夹中寻找模版。因此如果你的应用是个模块,这个文件夹在模块的旁边,如果它是一个包,那么这个文件夹在你的包里面:
Case 1: 一个模块:
/application.py
/templates
/hello.html
Case 2: 一个包:
/application
/__init__.py
/templates
/hello.html
对于模板,你可以使用 Jinja2 模板的全部功能。详细信息查看官方的 Jinja2 Template Documentation 。
这里是一个模版的例子:
<!doctype html>
<title>Hello from Flask</title>
{% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello World!</h1> {% endif %}
在模版中你也可以使用 request
, session
和 g
[1] 对象,也能使用函数 get_flashed_messages()
。
模版继承是十分有用的。如果想要知道模版继承如何工作的话,请阅读文档 模板继承 。基本的模版继承使得某些特定元素(如标题,导航和页脚)在每一页成为可能。
自动转义是开启的,因此如果 name 包含 HTML,它将会自动转义。如果你信任一个变量,并且你知道它是安全的 (例如一个模块把 wiki 标记转换到 HTML ),你可以用 Markup
类或 |safe
过滤器在模板中标记它是安全的。 在 Jinja 2 文档中,你会见到更多例子。
这是一个 Markup
类如何工作的基本介绍:
>>> from flask import Markup >>> Markup(''<strong>Hello %s!</strong>'') % ''<blink>hacker</blink>'' Markup(u''<strong>Hello <blink>hacker</blink>!</strong>'') >>> Markup.escape(''<blink>hacker</blink>'') Markup(u''<blink>hacker</blink>'') >>> Markup(''<em>Marked up</em> » HTML'').striptags() u''Marked up \xbb HTML''
Changed in version 0.5: 自动转义不再在所有模版中启用。模板中下列后缀的文件会触发自动转义:.html
, .htm
, .xml
, .xhtml
。从字符串加载的模板会禁用自动转义。
[1] | 不知道 g 对象是什么?它是可以按你的需求存储信息的东西,更多的信息查看 g 文档以及 在 Flask 中使用 SQLite 3。 |
接收请求数据
对于 web 应用来说,对客户端发送给服务器的数据做出反应至关重要。在 Flask 中由全局对象 request
来提供这些信息。如果你有一定的 Python 经验,你会好奇这个对象怎么可能是全局的,并且 Flask 是怎么还能保证线程安全。 答案是上下文作用域:
局部上下文
内幕消息
如果你想要了解它是如何工作以及如何用它实现测试,请阅读本节,否则请跳过本节。
Flask 中的某些对象是全局对象,但不是通常的类型。这些对象实际上是给定上下文的局部对象的代理。 虽然很拗口,但实际上很容易理解。
想象下线程处理的上下文。一个请求传入,web 服务器决定产生一个新线程(或者其它东西, 底层对象比线程更有能力处理并发系统)。当 Flask 开始它内部请求处理时,它认定当前线程是活动的上下文并绑定当前的应用和 WSGI 环境到那 个上下文(线程)。它以一种智能的方法来实现,以致一个应用可以调用另一个应用而不会中断。
所以这对你意味着什么了?如果你是做一些类似单元测试的事情否则基本你可以完全忽略这种情况。 你会发现依赖于请求对象的代码会突然中断,因为没有请求对象。解决方案就是自己创建一个请求并把它跟上下文绑定。 针对单元测试最早的解决方案是使用 test_request_context()
上下文管理器。结合 with 声明,它将绑定一个测试请求来进行交互。这里是一个例子:
from flask import request
with app.test_request_context(''/hello'', method=''POST''): # now you can do something with the request until the # end of the with block, such as basic assertions: assert request.path == ''/hello'' assert request.method == ''POST''
另一个可能性就是传入整个 WSGI 环境到 request_context()
方法:
from flask import request
with app.request_context(environ): assert request.method == ''POST''
请求对象
请求对象在 API 章节中描述,这里我们不再详细涉及(请看 request
)。这里对一些最常见的操作进行概述。 首先你需要从 flask 模块中导入它:
from flask import request
当前请求的方法可以用 method
属性来访问。你可以用 form
属性来访问表单数据 (数据在 POST 或者 PUT 中传输)。这里是上面提及到两种属性的完整的例子:
@app.route(''/login'', methods=[''POST'', ''GET'']) def login(): error = None if request.method == ''POST'': if valid_login(request.form[''username''], request.form[''password'']): return log_the_user_in(request.form[''username'']) else: error = ''Invalid username/password'' # the code below this is executed if the request method # was GET or the credentials were invalid return render_template(''login.html'', error=error)
如果在 form 属性中不存在上述键值会发生些什么?在这种情况下会触发一个特别的 KeyError
。 你可以像捕获标准的 KeyError
来捕获它,如果你不这样去做,会显示一个 HTTP 400 Bad Request 错误页面。 所以很多情况下你不需要处理这个问题。
你可以用 args
属性来接收在 URL ( ?key=value
) 中提交的参数:
searchword = request.args.get(''key'', '''')
我们推荐使用 get 来访问 URL 参数或捕获 KeyError ,因为用户可能会修改 URL, 向他们显示一个 400 bad request 页面不是用户友好的。
想获取请求对象的完整的方法和属性清单,请参阅 request
的文档。
文件上传
你能够很容易地用 Flask 处理文件上传。只要确保在你的 HTML 表单中不要忘记设置属性 enctype="multipart/form-data"
, 否则浏览器将不传送文件。
上传的文件是存储在内存或者文件系统上一个临时位置。你可以通过请求对象中 files
属性访问这些文件。 每个上传的文件都会存储在这个属性字典里。它表现得像一个标准的 Python file
对象,但是它同样具有 save()
方法,该方法允许你存储文件在服务器的文件系统上。 这儿是一个简单的例子展示如何工作的:
from flask import request
@app.route(''/upload'', methods=[''GET'', ''POST'']) def upload_file(): if request.method == ''POST'': f = request.files[''the_file''] f.save(''/var/www/uploads/uploaded_file.txt'') ...
如果你想要知道在上传到你的应用之前在客户端的文件名称,你可以访问 filename
属性。但请记住永远不要信任这个值,因为这个值可以伪造。如果你想要使用客户端的文件名来在服务器上存储文件, 把它传递到 Werkzeug 提供给你的 secure_filename()
函数:
from flask import request
from werkzeug import secure_filename @app.route(''/upload'', methods=[''GET'', ''POST'']) def upload_file(): if request.method == ''POST'': f = request.files[''the_file''] f.save(''/var/www/uploads/'' + secure_filename(f.filename)) ...
一些更好的例子,请查看 上传文件 。
Cookies
你可以用 cookies
属性来访问 cookies。你能够用响应对象的 set_cookie
来设置 cookies。请求对象中的 cookies
属性是一个客户端发送所有的 cookies 的字典。 如果你要使用会话(sessions),请不要直接使用 cookies 相反用 Flask 中的 会话,Flask 已经在 cookies 上增加了一些安全细节。
读取 cookies:
from flask import request
@app.route(''/'') def index(): username = request.cookies.get(''username'') # use cookies.get(key) instead of cookies[key] to not get a # KeyError if the cookie is missing.
存储 cookies:
from flask import make_response
@app.route(''/'') def index(): resp = make_response(render_template(...)) resp.set_cookie(''username'', ''the username'') return resp
注意 cookies 是在响应对象中被设置。由于通常只是从视图函数返回字符串, Flask 会将其转换为响应对象。 如果你要显式地这么做,你可以使用 make_response()
函数接着修改它。
有时候你可能要在响应对象不存在的地方设置 cookie。利用 延迟请求回调 模式使得这种情况成为可能。
为此也可以参阅 关于响应。
重定向和错误
你能够用 redirect()
函数重定向用户到其它地方。能够用 abort()
函数提前中断一个请求并带有一个错误代码。 这里是一个它们如何工作的例子:
from flask import abort, redirect, url_for @app.route(''/'') def index(): return redirect(url_for(''login'')) @app.route(''/login'') def login(): abort(401) this_is_never_executed()
这是一个相当无意义的例子因为用户会从主页重定向到一个不能访问的页面( 401意味着禁止访问), 但是它说明了重定向如何工作。
默认情况下,每个错误代码会显示一个黑白错误页面。如果你想定制错误页面,可以使用 errorhandler()
装饰器:
from flask import render_template
@app.errorhandler(404) def page_not_found(error): return render_template(''page_not_found.html''), 404
注意到 404
是在 render_template()
调用之后。告诉 Flask 该页的错误代码应是 404 , 即没有找到。默认的 200 被假定为:一切正常。
关于响应
一个视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串,它被转换成一个响应主体是该字符串,错误代码为 200 OK
,媒体类型为 text/html
的响应对象。 Flask 把返回值转换成响应对象的逻辑如下:
- 如果返回的是一个合法的响应对象,它会被从视图直接返回。
- 如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
- 如果返回的是一个元组而且元组中元素能够提供额外的信息。这样的元组必须是
(response, status, headers)
形式且至少含有一个元素。 status 值将会覆盖状态代码,headers 可以是一个列表或额外的消息头值字典。 - 如果上述条件均不满足,Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象。
如果你想要获取在视图中得到的响应对象,你可以用函数 make_response()
。
想象你有这样一个视图:
@app.errorhandler(404)
def not_found(error): return render_template(''error.html''), 404
你只需要用 make_response()
封装返回表达式,获取结果对象并修改,然后返回它:
@app.errorhandler(404)
def not_found(error): resp = make_response(render_template(''error.html''), 404) resp.headers[''X-Something''] = ''A value'' return resp
会话
除了请求对象,还有第二个称为 session
对象允许你在不同请求间存储特定用户的信息。 这是在 cookies 的基础上实现的,并且在 cookies 中使用加密的签名。这意味着用户可以查看 cookie 的内容, 但是不能修改它,除非它知道签名的密钥。
要使用会话,你需要设置一个密钥。这里介绍会话如何工作:
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) @app.route(''/'') def index(): if ''username'' in session: return ''Logged in as %s'' % escape(session[''username'']) return ''You are not logged in'' @app.route(''/login'', methods=[''GET'', ''POST'']) def login(): if request.method == ''POST'': session[''username''] = request.form[''username''] return redirect(url_for(''index'')) return '''''' <form action="" method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> '''''' @app.route(''/logout'') def logout(): # remove the username from the session if it''s there session.pop(''username'', None) return redirect(url_for(''index'')) # set the secret key. keep this really secret: app.secret_key = ''A0Zr98j/3yX R~XHH!jmN]LWX/,?RT''
这里提到的 escape()
可以在你不使用模板引擎的时候做转义(如同本例)。
怎样产生一个好的密钥
随机的问题在于很难判断什么是真随机。一个密钥应该足够随机。你的操作系统可以基于一个密码随机生成器来生成漂亮的随机值,这个值可以用来做密钥:
>>> import os
>>> os.urandom(24) ''\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8''
把这个值复制粘贴到你的代码,你就搞定了密钥。
使用基于 cookie 的会话需注意: Flask 会将你放进会话对象的值序列化到 cookie。如果你试图寻找一个跨请求不能存留的值, cookies 确实是启用的,并且你不会获得明确的错误信息,检查你页面请求中 cookie 的大小,并与 web 浏览器所支持的大小对比。
消息闪烁
好的应用和用户界面全部是关于反馈。如果用户得不到足够的反馈,他们可能会变得讨厌这个应用。Flask 提供了一个 真正的简单的方式来通过消息闪现系统给用户反馈。消息闪现系统基本上使得在请求结束时记录信息并在下一个 (且仅在下一个)请求中访问。通常结合模板布局来显示消息。
使用 flash()
方法来闪现一个消息,使用 get_flashed_messages()
能够获取消息,get_flashed_messages()
也能用于模版中。针对一个完整的例子请查阅 消息闪现。
日志
New in version 0.3.
有时候你会处于一种你处理的数据应该是正确的,然而实际上并不正确的状况。比如你可能有一些客户端代码, 代码向服务器发送一个 HTTP 请求但是显然它是畸形的。这可能是由于用户篡改数据,或客户端代码失败。 大部分时候针对这一情况返回 400 BadRequest
就可以了,但是有时候不行因为代码必须继续工作。
你可能仍然想要记录发生什么不正常事情。这时候日志就派上用处。从 Flask 0.3 开始日志记录是预先配置好的。
这里有一些日志调用的例子:
app.logger.debug(''A value for debugging'') app.logger.warning(''A warning occurred (%d apples)'', 42) app.logger.error(''An error occurred'')
附带的 logger
是一个标准的日志类 Logger
,因此更多的信息请 查阅官方文档 logging documentation。
整合 WSGI 中间件
如果你想给你的应用添加 WSGI 中间件,你可以封装内部 WSGI 应用。例如如果你想使用 Werkzeug 包中的某个中间件来应付 lighttpd 中的 bugs,你可以这样做:
from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
部署到 Web 服务器
准备好部署你的新 Flask 应用?你可以立即部署到托管平台来完成快速入门,以下是向小项目提供免费的方案:
- Deploying Flask on Heroku
- Deploying WSGI on dotCloud with Flask-specific notes
你可以托管 Flask 应用的其它选择:
- Deploying Flask on Webfaction
- Deploying Flask on Google App Engine
- Sharing your Localhost Server with Localtunnel
如果你管理你自己的主机并且想要自己运行,请参看 部署方式。
Python Flask Web框架教程 2 模板
原文
在完成第1章之后,你将拥有一个具有以下文件结构的可正常运行且简单的Web应用程序:
microblog\
venv\
app\
__init__.py
routes.py
microblog.py
要运行该应用程序,请在终端会话中设置FLASK_APP = microblog.py
,然后执行flask run
。 这将使用该应用程序启动Web服务器,你可以通过在Web浏览器的地址栏中键入http://localhost:5000/
URL来打开该服务器。
在本章中,你将继续使用同一应用程序,特别是,你将学习如何生成具有复杂结构和许多动态组件的更精致的网页。 如果到目前为止尚不清楚有关应用程序或开发工作流程的任何内容,请在继续之前再次阅读第1章。
本章的GitHub链接是:浏览,压缩,差异。
What Are Templates?
我希望我的微博应用程序的首页具有欢迎用户的标题。 目前,我将忽略以下事实:该应用程序还没有用户概念,因为这将在以后出现。 相反,我将使用模拟用户,该用户将作为Python字典实现,如下所示:
user = {'username': 'Miguel'}
创建模拟对象是一种有用的技术,它使你可以专注于应用程序的一部分,而不必担心系统中尚不存在的其他部分。 我想设计应用程序的主页,也不希望没有适当的用户系统来分散我的注意力,所以我只是组成了一个用户对象,以便继续前进。
应用程序中的view函数返回一个简单的字符串。 我现在想做的是将返回的字符串扩展为完整的HTML页面,也许是这样的:
#app/routes.py: Return complete HTML page from view function
from app import app
@app.route('/')
@app.route('/index')
def index():
user = {'username': 'Miguel'}
return '''
<html>
<head>
<title>Home Page - Microblog</title>
</head>
<body>
<h1>Hello, ''' + user['username'] + '''!</h1>
</body>
</html>'''
如果你不熟悉HTML,建议你阅读Wikipedia上的HTML标记以作简要介绍。
如上所述更新视图功能,并尝试在浏览器中查看应用程序的外观。
我希望你同意我的观点,以上将HTML传输到浏览器的解决方案不是很好。考虑一下当我收到来自用户的博客文章时,此视图功能中的代码将变得多么复杂,并且这些博客文章将不断变化。该应用程序还将具有更多的视图功能,这些视图功能将与其他URL关联,因此想象一下,如果有一天我决定更改此应用程序的布局,并且必须在每个视图功能中更新HTML。显然,这不是随应用程序的增长而扩展的选项。
如果你可以将应用程序的逻辑与网页的布局或表示形式分开,那么事情就会井井有条,不是吗?在用Python编写应用程序逻辑时,你甚至可以雇用一名Web设计人员来创建一个杀手级网站。
模板有助于实现表示和业务逻辑之间的分离。在Flask中,模板被写为单独的文件,存储在应用程序包内的templates
文件夹中。因此,在确保你位于microblog
目录中之后,创建用于存储模板的目录:
(venv) $ mkdir app/templates
在下面,你可以看到你的第一个模板,其功能与上面的index()
视图函数返回的HTML页面相似。 将此文件写入app/templates/index.html
中:
<!--app/templates/index.html: Main page template/-->
<html>
<head>
<title>{{ title }} - Microblog</title>
</head>
<body>
<h1>Hello, {{ user.username }}!</h1>
</body>
</html>
这是一个非常标准的HTML页面。 此页面上唯一有趣的事情是,在{{...}}
部分中包含了几个用于动态内容的占位符。 这些占位符代表页面的可变部分,只有在运行时才知道。
现在,页面的显示已被卸载到HTML模板,可以简化视图功能:
#app/routes.py: Use render\_template() function
from flask import render_template
from app import app
@app.route('/')
@app.route('/index')
def index():
user = {'username': 'Miguel'}
return render_template('index.html', title='Home', user=user)
这样看起来好多了,对吗? 尝试使用此新版本的应用程序以查看模板的工作方式。 在浏览器中加载页面后,你可能需要查看源HTML并将其与原始模板进行比较。
将模板转换为完整的HTML页面的操作称为渲染(rendering)。 为了渲染模板,我必须导入Flask框架随附的称为render_template()
的函数。 此函数采用模板文件名和模板参数的变量列表,并返回相同的模板,但其中的所有占位符均替换为实际值。
render_template()
函数将调用Flask框架随附的Jinja2模板引擎。 Jinja2用相应的值替换{{...}}
块,这些值由render_template()
调用中提供的参数给出。
Conditional Statements
你已经看到Jinja2如何在渲染过程中用实际值替换占位符,但这只是Jinja2在模板文件中支持的许多强大操作之一。 例如,模板还支持在{%...%}
块内给出的控制语句。 下一个index.html
模板版本添加了一条条件语句:
<!--app/templates/index.html: Conditional statement in template-->
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog!</title>
{% endif %}
</head>
<body>
<h1>Hello, {{ user.username }}!</h1>
</body>
</html>
现在,模板更加智能。 如果view函数忘记传递title
占位符变量的值,则模板将显示默认标题,而不是显示空标题。 你可以通过在视图函数的render_template()
调用中删除title
参数来尝试此条件的工作方式。
Loops
登录的用户可能希望在主页中查看已连接用户的最新帖子,因此我现在要做的就是扩展应用程序以支持该操作。
再一次,我将依靠方便的假对象技巧来创建一些用户和一些帖子来显示:
#app/routes.py: Fake posts in view function
from flask import render_template
from app import app
@app.route('/')
@app.route('/index')
def index():
user = {'username': 'Miguel'}
posts = [
{
'author': {'username': 'John'},
'body': 'Beautiful day in Portland!'
},
{
'author': {'username': 'Susan'},
'body': 'The Avengers movie was so cool!'
}
]
return render_template('index.html', title='Home', user=user, posts=posts)
为了表示用户帖子,我使用了一个列表,其中每个元素都是具有author
和body
字段的字典。 当我真正实现用户和博客文章时,我将尝试尽可能保留这些字段名称,以便使用这些假对象设计和测试主页模板的所有工作将继续进行。 在介绍真实用户和帖子时有效。
在模板方面,我必须解决一个新问题。 帖子列表可以包含任意数量的元素,由查看功能决定页面中将显示多少帖子。 该模板不能对有多少个帖子做出任何假设,因此需要准备好呈现与视图以通用方式发送的一样多的帖子。
对于此类问题,Jinja2提供了for
控件结构:
<!--app/templates/index.html: for-loop in template-->
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog</title>
{% endif %}
</head>
<body>
<h1>Hi, {{ user.username }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}
</body>
</html>
简单吧? 请尝试使用该新版本的应用程序,并确保将更多内容添加到帖子列表中,以查看模板如何适应并始终呈现视图功能发送的所有帖子。
Template Inheritance
如今,大多数Web应用程序在页面顶部都有一个导航栏,其中包含一些常用链接,例如用于编辑个人资料,登录,注销等的链接。我可以轻松地将导航栏添加到index.html
。 带有更多HTML的模板,但是随着应用程序的增长,我将在其他页面中需要相同的导航栏。 我并不是很想在许多HTML模板中维护导航栏的多个副本,如果可能的话,最好不要重复自己的做法。
Jinja2具有专门解决此问题的模板继承功能。 本质上,你可以做的是将页面布局中所有模板共有的部分移动到基本模板,所有其他模板都从该基本模板中派生。
因此,我现在要做的是定义一个名为base.html
的基本模板,该模板包括一个简单的导航栏以及我之前实现的标题逻辑。 你需要在文件app/templates/base.html
中编写以下模板:
<!--app/templates/base.html: Base template with navigation bar-->
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog</title>
{% endif %}
</head>
<body>
<div>Microblog: <a href="/index">Home</a></div>
<hr>
{% block content %}{% endblock %}
</body>
</html>
在此模板中,我使用了block
控制语句来定义派生模板可插入自身的位置。 块具有唯一的名称,派生的模板在提供其内容时可以引用这些名称。
有了基本模板之后,我现在可以通过使其继承自base.html
来简化index.html
:
<!--app/templates/index.html: Inherit from base template-->
{% extends "base.html" %}
{% block content %}
<h1>Hi, {{ user.username }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}
{% endblock %}
由于base.html
模板现在将处理常规的页面结构,因此我从index.html
中删除了所有这些元素,仅保留了内容部分。 extend
语句在两个模板之间建立继承链接,因此Jinja2知道当要求呈现index.html
时,需要将其嵌入base.html
中。 这两个模板具有匹配的带有名称content
的block
语句,这就是Jinja2知道如何将两个模板组合为一个的方式。 现在,如果需要为该应用程序创建其他页面,则可以将它们创建为来自同一base.html
模板的派生模板,这就是我可以使应用程序的所有页面共享相同外观的方式,而无需重复。
Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架
回复“书籍”即可获赠Python从入门到进阶共10本电子书
今
日
鸡
汤
0
前言
前几天给大家分别分享了(入门篇)简析Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架和(进阶篇)Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架。今天欢迎大家来到 FastAPI 系列分享的完结篇,本文主要是对于前面文章的补充和扩展。
当然这些功能在实际开发中也扮演者极其重要的角色。
中间件的使用
Flask 有 钩子函数,可以对某些方法进行装饰,在某些全局或者非全局的情况下,增添特定的功能。
同样在 FastAPI 中也存在着像钩子函数的东西,也就是中间件 Middleware了。
计算回调时间
# -*- coding: UTF-8 -*-
import time
from fastapi import FastAPI
from starlette.requests import Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
print(response.headers)
return response
@app.get("/")
async def main():
return {"message": "Hello World"}
if __name__ == ''__main__'':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
请求重定向中间件
from fastapi import FastAPI
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
# 被重定向到 301
@app.get("/")
async def main():
return {"message": "Hello World"}
授权允许 Host 访问列表(支持通配符匹配)
from fastapi import FastAPI
from starlette.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)
@app.get("/")
async def main():
return {"message": "Hello World"}
跨域资源共享
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
app = FastAPI()
#允许跨域请求的域名列表(不一致的端口也会被视为不同的域名)
origins = [
"https://gzky.live",
"https://google.com",
"http://localhost:5000",
"http://localhost:8000",
]
# 通配符匹配,允许域名和方法
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
在前端 ajax 请求,出现了外部链接的时候就要考虑到跨域的问题,如果不设置允许跨域,浏览器就会自动报错,跨域资源 的安全问题。
所以,中间件的应用场景还是比较广的,比如爬虫,有时候在做全站爬取时抓到的 Url 请求结果为 301,302, 之类的重定向状态码,那就有可能是网站管理员设置了该域名(二级域名) 不在 Host 访问列表 中而做出的重定向处理,当然如果你也是网站的管理员,也能根据中间件做些反爬的措施。
更多中间件参考 https://fastapi.tiangolo.com/advanced/middleware
BackgroundTasks
创建异步任务函数,使用 async 或者普通 def 函数来对后端函数进行调用。
发送消息
# -*- coding: UTF-8 -*-
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: str = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}
使用方法极其的简单,也就不多废话了,write_log 当成 task 方法被调用,先方法名,后传参。
自定义 Response 状态码
在一些特殊场景我们需要自己定义返回的状态码
from fastapi import FastAPI
from starlette import status
app = FastAPI()
# 201
@app.get("/201/", status_code=status.HTTP_201_CREATED)
async def item201():
return {"httpStatus": 201}
# 302
@app.get("/302/", status_code=status.HTTP_302_FOUND)
async def items302():
return {"httpStatus": 302}
# 404
@app.get("/404/", status_code=status.HTTP_404_NOT_FOUND)
async def items404():
return {"httpStatus": 404}
# 500
@app.get("/500/", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
async def items500():
return {"httpStatus": 500}
if __name__ == ''__main__'':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
这么一来就有趣了,设想有个人写了这么一段代码
async def getHtml(self, url, session):
try:
async with session.get(url, headers=self.headers, timeout=60, verify_ssl=False) as resp:
if resp.status in [200, 201]:
data = await resp.text()
return data
except Exception as e:
print(e)
pass
那么就有趣了,这段获取 Html 源码的函数根据 Http状态码 来判断是否正常的返回。那如果根据上面的写法,我直接返回一个 404 或者 304 的状态码,但是响应数据却正常,那么这个爬虫岂不是什么都爬不到了么。所以,嘿嘿你懂的!!
关于部署
部署 FastAPI 应用程序相对容易
Uvicorn
FastAPI 文档推荐使用 Uvicorn 来部署应用( 其次是 hypercorn),Uvicorn 是一个基于 asyncio 开发的一个轻量级高效的 Web 服务器框架(仅支持 python 3.5.3 以上版本)
安装
pip install uvicorn
启动方式
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Gunicorn
如果你仍然喜欢用 Gunicorn 在部署项目的话,请看下面
安装
pip install gunicorn
启动方式
gunicorn -w 4 -b 0.0.0.0:5000 manage:app -D
Docker部署
采用 Docker 部署应用的好处就是不用搭建特定的运行环境(实际上就是 docker 在帮你拉取),通过 Dockerfile 构建 FastAPI 镜像,启动 Docker 容器,通过端口映射可以很轻松访问到你部署的应用。
Nginx
在 Uvicorn/Gunicorn + FastAPI 的基础上挂上一层 Nginx 服务,一个网站就可以上线了,事实上直接使用 Uvicorm 或 Gunicorn 也是没有问题的,但 Nginx 能让你的网站看起来更像网站。
撒花 !!!
------------------- End -------------------
往期精彩文章推荐:
一篇文章教会你用Python抓取抖音app热点数据
手把手教你进行pip换源,让你的Python库下载嗖嗖的
手把手教你用免费代理ip爬数据

欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持
想加入Python学习群请在后台回复【入群】
万水千山总是情,点个【在看】行不行
/今日留言主题/
说一两个你知道的Python框架吧~
本文分享自微信公众号 - Python爬虫与数据挖掘(crawler_python)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
关于python web框架Flask——csrf攻击和php csrf攻击的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于flask——CSRFToken保护、pthon web框架flask(二)--快速入门、Python Flask Web框架教程 2 模板、Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架等相关知识的信息别忘了在本站进行查找喔。
本文标签: