前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从0到1,Flask全网最全教学!全文1w字,蓝图、会话、日志、部署等使用Flask搭建中小型企业级项目

从0到1,Flask全网最全教学!全文1w字,蓝图、会话、日志、部署等使用Flask搭建中小型企业级项目

原创
作者头像
小羽网安
发布2024-06-27 13:01:13
4490
发布2024-06-27 13:01:13
举报
文章被收录于专栏:编程

从0到1,Flask全网最全教学!全文1w字,蓝图、会话、日志、部署等使用Flask搭建中小型企业级项目

什么是flask?
image-20240627130025500
image-20240627130025500

Flask是一个使用Python编写的轻量级Web应用框架,它简洁而灵活,适用于开发小型至中型的Web应用。本文将介绍Flask框架的基本概念、特点以及如何使用Flask来快速搭建Web应用,争取在两周内,介绍一篇企业级响应速度的轻量级python Web框架sanic和异步数据库SQLAlchemy

2024年6月27日找工作日记,21岁,简历--》https://xiaoyus.cc/index.php/sample-page/

优点
  • 轻量级: 框架本身轻量,但支持使用扩展构建复杂的应用。
  • 灵活性: 提供更大的灵活性,开发者可以根据项目需求选择使用的组件。
  • RESTful可请求调度、Jinja2模板、支持安全cookie(客户端会话)、符合WSGI 1.0
缺点
  • 缺少一些内置功能: 相对于Django,一些功能需要通过扩展实现。
  • 适用于中小型项目: 对于大型企业级应用,可能需要更多的手动配置。
  • 在异步请求响应速度方面,没有sanic
安装 Flask

在已激活的虚拟环境中可以使用如下命令安装 Flask:

代码语言:javascript
复制
pip install Flask

1、创建第一个视图函数

代码语言:javascript
复制
# 导入flask类
from flask import Flask
# 初始化
app = Flask(__name__)
# 使用route()装饰器来告诉Flask触发函数的URL。
@app.route('/')
def index():
    return 'Hello World!'

Tips:你创建的第一个flask-py文件不能为flask.py,这会与flask本身发生冲突。

启动

可以使用flask命令或者python -m flask来运行这个应用。你需要使用--app选项告诉flask 你的应用名称是什么

代码语言:javascript
复制
flask --app main run

Tips:如果文件名为 app.py 或者 wsgi.py ,那么就不 需要使用 --app

image-20240625225907504
image-20240625225907504

出现了这个错误警告,跳过就好,不影响falsk运行,错误原因是因为在开发环境中,Flask应用程序是使用内置的服务器(如SimpleServerLighttpd)运行的,而不是使用WSGI服务器。

好啦,打开我们的蓝色链接,我们第一个flask程序就写好了

image-20240625231030783
image-20240625231030783
外部服务器(--host)

运行服务的时候,只能本地访问,而网络中的其他电脑却访问不了。缺省(默认)设置就是这样的,我们可以加上--host=0.0.0.0

代码语言:javascript
复制
flask --app main run --host=0.0.0.0
image-20240625231641102
image-20240625231641102

这时候多了一条外网地址,我们使用虚拟机访问一下,访问成功!

image-20240625231907533
image-20240625231907533
调试模式( --debug)
代码语言:javascript
复制
flask --app main run --host=0.0.0.0 --debug
image-20240625232333432
image-20240625232333432
image-20240625232111133
image-20240625232111133
HTML 转义

当返回 HTML ( Flask 中的默认响应类型)时,为了防止注入攻击,所有用户 提供的值在输出渲染前必须被转义。使用Jinja(这个稍后会介绍)渲染的 HTML 模板会自动执行此操作。

代码语言:javascript
复制
from markupsafe import escape
​
@app.route("/<name>")# 路由中的 <name> 从 URL 中捕获值并将其传递给视图函数。
def hello(name):
    return f"Hello, {escape(name)}!" #  escape() 可以手动转义.
image-20240625232957173
image-20240625232957173

那么如果一个用户想要提交其名称为 <script>alert("bad")</script>escape就会转义为文本,预防XSS攻击

XSS攻击详见https://mp.weixin.qq.com/s/RJcOZuscU07BEPgK89LSrQ

image-20240625232948956
image-20240625232948956
路由

现代 web 应用都使用有意义的 URL ,这样有助于用户记忆,网页会更得到用 户的青睐,提高回头率。

使用 route() 装饰器来把函数绑定到 URL:

代码语言:javascript
复制
@app.route('/')
def index():
    return '登录页面'
​
@app.route('/index')
def hello():
    return 'Hello, World'
​
# 可以为一个函数指定多个规则
@app.route('/page1')
@app.route('/page2')
@app.route('/page3')
def page_handler():
    return 'This is a sample page.'
image-20240625234006055
image-20240625234006055
image-20240625234019609
image-20240625234019609
image-20240625234055347
image-20240625234055347
image-20240625234105490
image-20240625234105490
变量规则

通过把URL的一部分标记为<variable_name>就可以在URL中添加变量。标记的部分会作为关键字参数传递给函数。使用<converter:variable_name>,可以选择性的加上一个转换器,为变量指 定规则

代码语言:javascript
复制
@app.route('/user/<username>')
def show_user_profile(username):
    return f'用户名: {escape(username)}'
image-20240625235103809
image-20240625235103809
代码语言:javascript
复制
@app.route('/get/<int:get_id>')
def show_post(get_id):
    return f'Get请求id: {get_id}'
image-20240625235029280
image-20240625235029280
代码语言:javascript
复制
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    return f'path {escape(subpath)}'
image-20240625235638579
image-20240625235638579

转换器类型

介绍

string

(缺省||默认值) 接受任何不包含斜杠的文本

int

接受正整数

float

接受正浮点数

path

类似 string ,但可以包含斜杠

uuid

接受 UUID 字符串

唯一的URL/重定向行为
代码语言:javascript
复制
@app.route('/projects/') # 这里有斜杠
def projects():
    return 'The project page'
​
@app.route('/about') # 这里没有斜杠
def about():
    return 'The about page'

projects 的 URL 是中规中矩的,尾部有一个斜杠,看起来就如同一个文 件夹。访问一个没有斜杠结尾的 URL ( /projects )时 Flask 会自动进 行重定向,帮您在尾部加上一个斜杠( /projects/ ),比如访问这个urlhttp://127.0.0.1:5000/projects会自动重定向到http://127.0.0.1:5000/projects/,俩者只有一个斜杠之分

但是,如果访问http://127.0.0.1:5000/about/about这个路由后面添加了一个斜杠,就会导致404

image-20240626000214019
image-20240626000214019
URL构建

url_for函数用于构建指定函数的URL。它把函数名称作为第一个参数。可以接受任意个关键字参数,每个关键参数对应url中的变量。未知变量将添加到URL中作为查询参数。

代码语言:javascript
复制
@app.route('/index/')
def index333():
    print(url_for('index333')) # /index/
    # 获取index333视图函数的路由,必须传值sid=xxx,则返回值 /index2/22/
    return url_for('index222',sid=22)
​
@app.route('/index2/<int:sid>/')
def index222(sid):
    # 获取index333视图函数的路由,并传值sid=sid,假设sid=234 则返回值 /index/?sid=234
    # 这里可以传任意值
    return url_for('index333',sid=sid)
​
@app.route('/')
def index():
    print(url_for('index')) # /
    print(url_for('login')) # /login
    
    return 'index' 
​
@app.route('/login')
def login():
    print(url_for('login')) # /login
    print(url_for('index')) # /
    print(url_for('profile', username='zhang san'))# /user/zhang%20san
    return 'login' 
​
@app.route('/user/<username>')
def profile(username):
    print(url_for('login', next='/')) # /login?next=/
    print(url_for('index')) # /
    print(url_for('profile', username='zhang san'))# /user/zhang%20san
    return f'{username}\'s profile' 

为什么不在把 URL 写死在模板中,而要使用反转函数 url_for() 动态构建?

集中管理URL:通过反转URL,可以在单一位置修改URL,无需在代码库中四处搜寻,实现高效维护。 自动转义特殊字符:URL创建机制自动处理特殊字符的转义,确保URL的准确性,直观性和安全性。 生成绝对路径:使用URL生成器生成的路径始终是绝对路径,有效避免因相对路径引起的潜在问题。 适应子路径部署:当您的应用部署在URL的子路径下(例如部署在/myapplication而非根路径/),url_for()函数能够智能处理,确保URL的正确性。

HTTP方法

Web应用程序使用不同的HTTP方法处理URL。缺省情况下,一个路由只回应GET请求。可以使用route()装饰器的methods参数来处理不同的HTTP方法。

代码语言:javascript
复制
from flask import request
# 将所有函数都封装到同一个函数中,当每个方法都使用一些共同的数据时,这样是有用的
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()
    
# 你也可以把不同方法对应的视图分别放在独立的函数中。
@app.get('/login')
def login_get():
    return show_the_login_form()
​
@app.post('/login')
def login_post():
    return do_the_login()
​
。。。@app.[其他方法]
静态文件
代码语言:javascript
复制
url_for('static', filename='style.css') # /static/style.css
渲染模板

直接在Python中编写HTML不仅麻烦,还容易忽视安全问题。Flask通过集成Jinja2,简化了HTML的生成,自动进行转义,让开发更安全、更高效。

  1. 多用途文本生成:模板技术不仅适用于创建HTML网页,同样能够用来生成包括Markdown、纯文本等在内的多种文本文件格式。
  2. 网页内容生成:在web应用开发中,模板引擎是生成HTML页面的关键工具,但它们的应用远不止于此。
  3. 扩展性:模板系统的设计使其能够轻松扩展到其他文本格式,如电子邮件所需的纯文本格式,提供灵活性。
  4. 多样化应用:模板的通用性使其成为处理不同文本输出需求的理想选择,无论是在线内容还是电子邮件通信。

Tips:flask会在templates文件夹内寻找模板。

情况一:一个模块 /application.py /templates /hello.html 情况二:一个包 /application /__init__.py /templates /hello.html

构造你的视图函数

代码语言:javascript
复制
@app.route('/')
def index():
    txt_h1='hellow'
    return render_template('index.html',txt_h1=txt_h1)

flask模板语法和django中的模板语法类似,使用模板语法

代码语言:javascript
复制
<!doctype html>
<html lang="en">
<head>
    <title>这是你的第一个模板html</title>
</head>
<body>
    <h1>{{ txt_h1 }}</h1>
    {% if txt_h1=='hellow' %}
    <h1>h1标题存在hellow</h1>
    {% else %}
    <h1>h1标题不存在hellow</h1>
    {% endif %}
</body>
</html>
image-20240626163715670
image-20240626163715670

在模板内部可以像使用 url_for()get_flashed_messages() 函数一样访问 configrequestsessiong对象,自动转义缺省开启,确保安全性,如果 name 包含 HTML ,那么会被自动转义。 如果您可以信任某个变量,且知道它是安全的 HTML (例如变量来自一个把 wiki 标记转换为 HTML 的模块),那么可以使用 Markup 类把它标记为安全的,或者在模板中使用 |safe 过滤器,例如

代码语言:javascript
复制
@app.route('/')
def index():
    txt_h1='<a href="https://xiaoyus.cc">我的主页</a>'
    return render_template('index.html',txt_h1=txt_h1)
image-20240626164605413
image-20240626164605413

使用Markup

代码语言:javascript
复制
from markupsafe import Markup
​
@app.route('/')
def index():
    txt_h1='<a href="https://xiaoyus.cc">我的主页</a>'
    txt_h1=Markup(txt_h1) # 使用 Markup 类把它标记为安全的
    return render_template('index.html',txt_h1=txt_h1)
image-20240626165040085
image-20240626165040085

使用 | safe

代码语言:javascript
复制
<body>
    <h1>{{ txt_h1 | safe }}</h1><!--将txt_h1变量标记为安全-->
    {% if txt_h1=='hellow' %}
    <h1>h1标题存在hellow</h1>
    {% else %}
    <h1>h1标题不存在hellow</h1>
    {% endif %}
</body>
image-20240626165328437
image-20240626165328437
操作请求数据

在Flask中,全局对象request确实提供了客户端的请求信息,但可能让人疑惑的是,它如何保证在多线程环境下的线程安全。Flask通过使用本地环境(Local Environment)来解决这个问题。每个线程都拥有自己的本地环境,这意味着每个线程的request对象是独立的,不会与其他线程的request对象冲突。这样,即使在多线程环境下,每个请求也能安全地处理,而不会相互干扰。

本地环境

Flask中有些对象看似全局,实则不然。它们是本地对象的代理,意味着每个线程有自己的版本,从而保证了线程安全。这在单元测试时特别有用,因为测试时可能没有真正的请求对象。

要解决这个问题,你可以手动创建一个请求对象。Flask提供了test_request_context(),一个方便的环境管理器,让你可以在测试代码中模拟请求。使用它,你可以这样写:

代码语言:javascript
复制
from flask import Flask, request
​
app = Flask(__name__)
​
with app.test_request_context('/hello', method='POST'):
    # 在这个区块内,你可以像处理真实请求一样使用request对象
    assert request.path == '/hello'
    assert request.method == 'POST'

如果需要模拟更完整的环境,比如整个WSGI环境,可以使用request_context(),传入环境变量即可:

代码语言:javascript
复制
with app.request_context(environ):
    # 这里可以进行更复杂的测试
    assert request.method == 'POST'

简而言之,Flask让你即使在没有真实请求的情况下,也能轻松测试依赖于请求对象的代码。

请求对象

导入flask的request

代码语言:javascript
复制
from flask import request

通过使用method方法,处理当前请求方式

代码语言:javascript
复制
@app.route('/',methods=['GET','POST'])
def index():
    if request.method == 'GET':
        txt_h1='这是GET请求'
    elif request.method =="POST" :
        txt_h1='这是post请求'
    else:
        txt_h1='这是不支持的请求'
image-20240626171526997
image-20240626171526997

通过使用form方法,处理获取的请求方式数据

代码语言:javascript
复制
@app.route('/',methods=['GET','POST'])
def index():
    if request.method == 'POST':
        username= request.form['username'] # 获取post表单的username数据
        password= request.form['password'] # 获取post表单的password
        return (f'<script>alert("账号:{username},'
                f'密码:{password}")</script>')
    # txt_h1=Markup(txt_h1)
    return render_template('index.html')

这是你的html表单

代码语言:javascript
复制
<form action="/" method="post">
    <input name="username" ><br>
    <input name="password" ><br>
    <button>登录</button>
</form>

操作URL获取参数比如https://127.0.0.1/index/?page=1 如何取得这个page参数

代码语言:javascript
复制
searchword = request.args.get('page', '') # 后面的''表示默认为空,这样渲染模板的时候就不会有NULL字样显示了

其他方法参考官方文档或者直接查看源代码

文件上传

使用Flask上传文件时,确保HTML表单包含enctype="multipart/form-data",否则文件无法上传,一个简单案例

代码语言:javascript
复制
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['file']
        f.save('upload/uploaded_file.jpg')
    return render_template('index.html')

html

代码语言:javascript
复制
<form action="/upload" method="post" enctype="multipart/form-data">
    <input name="file" type="file" >
    <button>上传文件</button>
</form>

如果想要把客户端的文件名作 为服务器上的文件名,可以通过 Werkzeug 提供的 secure_filename() 函数:

代码语言:javascript
复制
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['file']
        f.save(f'upload/{secure_filename}')
    return render_template('index.html')
Cookies

访问 cookies ,可以使用 request的cookies 属性。

代码语言:javascript
复制
​
from flask import request
​
@app.route('/')
def index():
    # 读取 cookies
    username = request.cookies.get('username')
    # 储存 cookies
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

打开浏览器f12,找到网络中的包即可查看

image-20240626190718359
image-20240626190718359
重定向和错误

使用redirect()可以重定向。使用abort()可以更早退出请求,并且返回错误代码:

代码语言:javascript
复制
from flask import abort, redirect, url_for
​
@app.route('/')
def index():
    return redirect(url_for('login'))  # 使用redirect()重定向
    
@app.route('/login')
def login():
    abort(401)  # 后面的代码将不会运行,直接返回401状态码
    return 'test'

让一个用户从索引页重定向到一个无法访问的页 面(401 表示禁止访问)。上例可以说明重定向和出错跳出是如何工作的。

自定义请求状态页面

代码语言:javascript
复制
@app.errorhandler(404)
def errorPage(error):
    return render_template('error.html'), 404
image-20240626193302582
image-20240626193302582
关于响应

在Flask中,视图函数的返回值会直接生成响应对象。若返回字符串,Flask将其转换为响应体,附带200 OK状态码和text/html内容类型。若返回字典或列表,Flask自动调用jsonify(),生成JSON格式的响应。这是Flask处理响应的基本规则。

  1. 直接响应:若视图函数返回的是一个Response对象,Flask将直接使用该对象作为HTTP响应返回给客户端。
  2. 字符串转换:若返回值是字符串,Flask会将其作为响应体内容,并结合默认参数创建一个响应对象返回。 @app.route('/') def index(): return "<h1>这是一个响应体内容,会解析html</h1>"
  3. 流式响应:如果视图函数返回的是迭代器或生成器,Flask将按流式响应处理,这意味着响应体内容将逐步发送给客户端。
  4. JSON响应:对于字典或列表类型的返回值,Flask会通过jsonify()函数将它们转换为JSON格式的响应对象。 @app.route('/') def index(): return {'data':'this is index'}
image-20240626203256480
image-20240626203256480
  1. 元组处理:返回值若为元组,它应至少包含一个元素,并且可以提供额外的响应信息,如状态码或头部信息。元组的格式可以是(body, status)(body, headers)(body, status, headers),其中status将覆盖默认的状态代码,headers则添加额外的响应头,一个简单的例子 @app.errorhandler(404) def errorPage(error): return 'sdfjlsdjf', 404 # 表示 ('sdfjlsdjf', 404)
  2. WSGI应用:如果返回值不符合以上任何一种类型,Flask将尝试将返回值作为WSGI应用来处理,并将其转换为一个响应对象。
JSON格式的API
代码语言:javascript
复制
@app.route("/")
def me_api():
    user = get_user()
    return {
        "username": user.username,
        "pwd": user.pwd,
        "image": url_for("user_image", filename=user.image),
    }# 可以使用字典
​
​
@app.route("/user_data")
def users_api():
    users = get_user()
    return [user.to_json() for user in users] # 也可以使用列表字典
会话session

Session对象提供了跨请求存储信息的能力。它基于密钥签名的cookie实现,用户能够查看但无法修改,除非拥有相应的密钥。这确保了存储在session中的数据安全,防止了恶意篡改。所以在使用session之前必须设置一个密匙

代码语言:javascript
复制
from flask import session
​
# 设置你的密匙
app.secret_key = b'036421afea2dd14dd4016bf71304cc9b332e0a011895f38f07f62505fc41ca41'
​
@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {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 method="post">
            <p><input type='text' name='username'>
            <p><input type='submit' value='Login'>
        </form>
    '''
​
@app.route('/logout')
def logout():
    # 删除用户名
    session.pop('username', None)
    return redirect(url_for('index'))

如果你不设置这个key也会报如下错误

image-20240626210131561
image-20240626210131561

可以在终端使用如下命令生成key

代码语言:javascript
复制
 python -c 'import secrets; print(secrets.token_hex())'
 '036421afea2dd14dd4016bf71304cc9b332e0a011895f38f07f62505fc41ca41'

Flask通过序列化会话对象的值并存储在cookie中来管理会话。如果请求中未能持续维护会话值,访问时可能失败且错误不明显。开发者应检查cookie大小是否符合浏览器限制。此外,Flask支持通过扩展实现的服务端会话,提供更高安全性,即使客户端禁用cookie也能维持会话状态。

CORS跨域

浏览器的同源策略(Same-Origin Policy)限制了跨域请求,如果不进行特殊处理,跨域请求将被浏览器拦截。

跨域请求提升了应用间的互操作性,尤其有利于前后端分离架构,允许独立开发和部署。但同时,它也可能引入XSS和CSRF等安全威胁,增加服务器负担,并可能因携带用户信息如cookies而泄露隐私。此外,预检请求可能导致网络延迟,影响性能。开发者需在实现时综合考虑安全性和性能,采取必要措施以保护数据和提升用户体验。

1、使用单个跨域 在Flask框架中,@app.route装饰器用于定义路由,包括指定的URL路径和允许的HTTP请求方法。而@cross_origin装饰器则用于配置跨域资源共享(CORS),允许指定哪些域名可以访问该资源,以及允许使用哪些HTTP方法。

如果两个装饰器都指定了请求方法,它们的作用是不同的。@app.route的指定方法决定了哪些HTTP请求能够触发关联的视图函数。相比之下,@cross_origin中的请求方法设置只影响跨域请求的响应,例如,它决定了在预检请求(preflight request)中哪些方法可以被告知客户端是被允许的。

因此,即使两者都提到了请求方法,它们并不冲突,因为它们服务于不同的目的。@app.route确保了请求方法的匹配,而@cross_origin则处理了跨域请求的额外安全考量。开发者在使用时应确保两者的设置正确无误,以避免不必要的访问控制问题。

代码语言:javascript
复制
from flask import Flask
from flask_cors import CORS, cross_origin
​
app = Flask(__name__)
​
@app.route('/api/some_end', methods=['GET'])
@cross_origin(methods=['POST'])
def some_endpoint():
    if request.method == 'GET':
        # 处理GET请求
        return '这是 GET 请求!'
    elif request.method == 'POST':
        # 处理POST请求
        return '这是 POST 请求!'

2、使用全局跨域

Flask中,可以通过安装flask-cors扩展来支持跨域请求。

代码语言:javascript
复制
pip install flask-cors

导入将app加入即可

代码语言:javascript
复制
from flask import Flask
from flask_cors import CORS
​
app = Flask(__name__)
CORS(app) # 1. 指定所有域
# CORS(app, origins='http://example.com')# 2. 通过CORS扩展的origins参数来指定允许的来源
​
-----
日志

1、应用错误处理

应用程序在运行过程中难免会遇到错误,即使代码本身无懈可击。可能的原因包括客户端断开连接、数据库负载过高、存储空间不足、硬件故障、后台服务过载、依赖库存在缺陷或网络连接问题等。这些问题都是服务器运维中常见的挑战。

2、错误日志工具

嗯,这里介绍几个扩展Sentry Sentry 可以统计重复错误,捕获堆栈数据和本地变量用于排错,并在发生新的错误时或 者按指定频度发送电子邮件。

代码语言:javascript
复制
pip install sentry-sdk[flask]

然后添加如下代码

代码语言:javascript
复制
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
# YOUR_DSN_HERE 需要被替换为在 Sentry 安装时获得的 DSN 值。
sentry_sdk.init('YOUR_DSN_HERE', integrations=[FlaskIntegration()])

安装好以后,内部服务出错信息会自动向 Sentry 报告,你会接收到出错通知。

3、默认日志logger类

下面是默认的日志系统logger类

代码语言:javascript
复制
app.logger.debug('一个调试器日志消息')
app.logger.warning('警告日志', 42)
app.logger.error('这是一个错误日志')

4、错误处理器

Flask通过HTTP状态码区分错误类型:400-499表示客户端错误,500-599表示服务器错误。

开发者可以通过注册错误处理器来自定义错误页面,这些处理器函数在特定错误发生时被调用,并返回个性化的响应。错误处理器接收一个HTTPException实例,但返回的响应状态码需手动设置。正确配置错误处理器,可以改善用户体验并提供调试信息。

5、注册出错处理器

通过使用errorhandler()装饰函数来注册或者稍后使用regist_error_handler()来注册。记得当返回响应的时候设置的状态码。

代码语言:javascript
复制
#使用errorhandler()装饰器来注册自定义错误页面
@app.route('/handle_exception')
@app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_exception():
    return '这是一个自定义错误请求页面',502
​
​
#使用regist_error_handler()来注册
@app.route('/handle_exception')
def handle_exception():
    return '这是一个自定义错误请求页面,使用register_error_handler()注册的',502
​
app.register_error_handler(502,handle_exception)

在Flask中,werkzeug.exceptions.HTTPException及其子类如BadRequest(其代码为400)可被注册和抛出。对于非标准HTTP代码,Werkzeug无法直接识别,因此不应直接注册这些异常。正确的做法是定义一个带有合适HTTP代码的HTTPException子类,然后在应用中注册并使用这个自定义异常类。

代码语言:javascript
复制
class InsufficientStorage(werkzeug.exceptions.HTTPException):
    code = 507
    description = 'Not enough storage space.'
def handle_507():
    return '服务器内部错误',507
​
app.register_error_handler(InsufficientStorage, handle_507)
raise InsufficientStorage()

出错处理器可被用于任何异常类的注册,除了 HTTPException子类或者 HTTP 状态码。 出错处理器可被用于特定类的注册,也可用于一个父类的所有子类的注册。

6、处理

在构建Flask应用时,您会遇到异常。如果在处理请求时(且没有注册 错误处理器),你的代码中断了,那么默认返回”500内部服务器错误“( InternalServerError)。同样,如果请求被发送到未注册的路由,则会产生”404未找到“(NotFound)错误。如果路由接收到被禁止请求方法,则会产生”405方法访问被禁止“(MethodNotAllowed)。Flask默认提供这些HTTPException

Flask 使您能够注册 Werkzeug 提供的任意 HTTP 异常。但是,默认的 HTTP 异 常返回简单的异常页。您可能希望在发生错误时向用户显示自定义错误页面可 以通过注册错误处理器来完成

7、通用异常处理器

可以为非常通用的基类注册异常处理器,例如HTTPException基类或者Exception基类。但是,请注意,这样会捕捉到超出你预期的异常。

基于 HTTPException 的异常处理器对于把缺省的 HTML 出错页面转换 为 JSON 非常有用,但是这个处理器会触发不由你直接产生的东西,如路由过程 中产生的 404 和 405 错误。请仔细制作你的处理器,确保不会丢失关于 HTTP 错误的信息。

代码语言:javascript
复制
from flask import json
from werkzeug.exceptions import HTTPException # 导入HTTPException
 
@app.errorhandler(HTTPException)
def handle_exception(e):
    """返回json,而不是html"""
    response = e.get_response()
    # 用json替换正文
    response.data = json.dumps({
        "code": e.code,
        "name": e.name,
        "description": e.description,
    })
    response.content_type = "application/json"
    return response
​
image-20240626234306743
image-20240626234306743

注意不能够这样写

代码语言:javascript
复制
# 注意这个返回结果是错误的,这里如果直接返回字典数据,并不会得到真实的状态码,而是200,他是一个请求数据
@app.errorhandler(HTTPException)
def handle_exception(e):
    response = e.get_response()
    return {
        "code": e.code,
        "name": e.name,
        "description": e.description,
    }
image-20240626234146718
image-20240626234146718

用于 Exception 的异常处理器有助于改变所有异常处理的表现形式,甚至包含 未处理的异常。但是,与在 Python 使用 except Exception: 类似,这样会捕 获 所有 未处理的异常,包括所有 HTTP 状态码。

因此,在大多数情况下,设定只针对特定异常的处理器比较安全。因为 HTTPException 实例是一个合法的 WSGI 响应,你可以直接传递该实例。

代码语言:javascript
复制
from werkzeug.exceptions import HTTPException
​
@app.errorhandler(Exception)
def handle_exception(e):
    # 跳过http异常
    if isinstance(e, HTTPException):
        return e
​
    # 只处理非http异常
    return render_template("500_generic.html", e=e), 500

异常处理器仍然遵循异常烦类的继承层次。如果同时基于 HTTPExceptionException 注册了异常处理器, Exception 处理器不会处理 HTTPException 子类,因为 HTTPException 更有针对性。

8、未处理的异常

当一个异常发生时,如果没有对应的异常处理器,那么就会返回一个500内部服务错误。

9、自定义错误页面

在Flask应用开发过程中,abort()函数是一个强大的工具,用于在检测到问题时立即终止请求并抛出一个HTTPException。这个函数不仅能够向用户明确地反馈错误,还能提供一个简洁的默认错误页面,帮助用户理解发生了什么。

例如,在处理用户配置文件的路由时,如果请求中缺少了用户名,我们可以使用abort(400)来告知用户请求不完整或格式错误。如果用户提供了用户名,但服务器上找不到对应的用户信息,我们则可以使用abort(404)来明确告知用户请求的资源不存在。

代码语言:javascript
复制
from flask import abort, render_template, request
from flask import render_template
​
@app.route("/profile")
def user_profile():
    username = request.arg.get("username")
    if username is None:
        abort(400) # 没有获取到username直接返回400状态码的,错误处理页面
​
    user = get_user(username=username)
    if user is None:
        abort(404) # 如果没有找到用户,就直接返回404页面
​
    return render_template("index.html", user=user)
​
​
@app.errorhandler(404)
def page_not_found(e):
    # 自定义错误页面
    return render_template('404.html'), 404

通过这种方式,abort()函数不仅帮助我们优雅地处理错误情况,还能够提供给用户清晰的反馈,增强了应用的用户体验和健壮性。此外,通过自定义错误处理函数,我们还可以进一步定制错误页面,提供更友好的错误信息和解决方案。

一个模板示例

代码语言:javascript
复制
{% extends "layout.html" %}
{% block title %}Page Not Found{% endblock %}
{% block body %}
  <h1>Page Not Found</h1>
  <p>What you were looking for is just not there.
  <p><a href="{{ url_for('index') }}">go somewhere nice</a>
{% endblock %}

10、将API错误作为JSON返回

代码语言:javascript
复制
from flask import abort, jsonify
​
@app.errorhandler(404)
def resource_not_found(e):
    return jsonify(error=str(e)), 404
​
@app.route("/cheese")
def get_one_cheese():
    resource = get_resource()
​
    if resource is None:
        abort(404, description="Resource not found")
​
    return jsonify(resource)

当然,还可以创建自定义异常类。

代码语言:javascript
复制
from flask import jsonify, request
​
#定义一个错误处理类,并继承Exception类
class InvalidAPIUsage(Exception):
    status_code = 400
​
    def __init__(self, message, status_code=None, payload=None):
        super().__init__()
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload
​
    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        return rv
​
#使用错误处理装饰器
@app.errorhandler(InvalidAPIUsage)
def invalid_api_usage(e):
    # 将错误 e 转换为dict,再转为json数据,后面接的请求状态码
    return jsonify(e.to_dict()), e.status_code
​
​
# 获取用户信息的API应用路由#正确的请求是/api/user?user_id=420
@app.route("/api/user")
def user_api(user_id):
    user_id = request.arg.get("user_id")
    if not user_id:
        raise InvalidAPIUsage("没有该用户id")
​
    user = get_user(user_id=user_id)
    if not user:
        raise InvalidAPIUsage("没有该用户!", status_code=404)
​
    return jsonify(user.to_dict())

一个视图现在可以引发带有错误信息的异常。此外,一些额外的内容可以通过 payload 参数,以字典的方式提供。

集成WSGI中间件

如果想要在应用中添加一个 WSGI 中间件,那么可以用应用的 wsgi_app 属性来包装。例如,假设需要在 Nginx 后面使用 ProxyFix 中间件,那么可以这样 做:

代码语言:javascript
复制
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)

app.wsgi_app 来包装,而不用 app 包装,意味着 app 仍旧 指向您的 Flask 应用,而不是指向中间件。这样可以继续直接使用和配置 app

消息闪现

在Web应用中,向用户反馈操作结果很关键,通常通过网页上的文字提示实现,如确认信息、警告或错误提示。

Flask中,使用flash message(闪现消息),具体使用的方法是flash()

代码语言:javascript
复制
flash(message, category)# message: 具体的消息内容 。category: 可选参数,表示消息类型,比如错误、警告等

在视图函数中发送了消息,自然的,就需要在模板文件中取出消息,我们使用方法get_flashed_message

代码语言:javascript
复制
get_flashed_messages(with_categories, category_filter)
  • with_categories: 消息类型,与上面的flash匹配
  • category_filter: 过滤条件 例如,
代码语言:javascript
复制
from flask import Flask, render_template, request, redirect, url_for, flash
​
app = Flask(__name__)
app.secret_key = "xxx"
​
​
@app.route('/')
def index():
    return render_template('index.html')
​
​
@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == "POST":
        if request.form['email'] != 'test@gmail.com' or request.form['password'] != 'test':
            error = "Invalid account."
        else:
            flash("Login successfully") # 当邮箱和密码输入正确的时候,调用`flash`方法
            return redirect(url_for('index'))
    
    return render_template('login.html', error=error)
​
​
if __name__ == '__main__':
    app.run(debug=True)
​

index.html模板文件

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    {% with messages = get_flashed_messages() %}
         {% if messages %}
               {% for message in messages %}
                    <p>{{ message }}</p>
               {% endfor %}
         {% endif %}
    {% endwith %}
​
<h3>Welcome!</h3>
<a href = "{{ url_for('login') }}">login</a>
</body>
</html>
​

login.html文件内容

代码语言:javascript
复制
@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == "POST":
        if request.form['email'] != '123@163.com' or request.form['password'] != '123':
            error = "Invalid account."
        else:
            flash("Login successfully")
            return redirect(url_for('index'))
​
    return render_template('login.html', error=error)

首页

image-20240627004224360
image-20240627004224360

登录失败

image-20240627004247932
image-20240627004247932

登录成功

image-20240627004204554
image-20240627004204554
蓝图(Blueprint)

本文介绍Flask蓝图,一种封装路由和视图函数的容器,用于模块化构建客户端请求与URL的映射,实现应用的组织

和扩展。 在Flask中,使用蓝图可以帮助我们实现模块化应用的功能,比如,现在有一个users.py和main.py实现用户的登录

users.py

代码语言:javascript
复制
from flask import Blueprint
​
user_app=Blueprint('user_app',__name__)
​
@user_app.route('/login')
def login():
    return '''
        <form action="/index">
        <input type="text" value="123"/>
        <button type="submit">Login</button>
        <form>
    '''
@user_app.route('/get_user')
def get_users():
    return {
        'username':'张三',
        'password':'123456',
    }

main.py

代码语言:javascript
复制
from flask import  Blueprint
from users import user_app
​
app = Flask(__name__)
app.register_blueprint(user_app)
​
@app.route('/index')
def index():
    return 'hello world'

运行效果,路由在不同文件中的时候也能够正常访问。

13m3k-komx2
13m3k-komx2
强大的Pandas数据分析库操作数据库、Excel、CSV等,配合flask使用

后续会出一期pandas详细使用教程,pandas,python+data+analysis的组合缩写,是python中基于numpy和matplotlib的第三方数据分析库,与后两者共同构成了python数据分析的基础工具包,享有数分三剑客之名,减少了我们使用原生sql语句的使用,如果数据量达到万级别,可以使用pandarallel来提升他的运行效率,介绍一个见到那的案例

代码语言:javascript
复制
import pandas as pd
import pymysql
​
@app.route('/')
def index():
    db_conn = pymysql.connect(
        host='localhost',
        port=3306,
        user='root',
        password='123456',
        database='demo',
        charset='utf8'
    )
​
    # 读取这个表的数据
    sql = "select * from t_user"
    df=pd.read_sql(sql, con=db_conn)
    # 开始处理这些数据
    df.drop_duplicates(inplace=True)# 去重
    # df.columns # 获取t_user的列名称
    # df.index # 获取t_user的索引
    # df=df.to_dict()# 将df转化为字典
    # sql = "select * from dog"
    # df_dog=pd.read_sql(sql, con=db_conn)
    # df.merge(df_dog, on='id', how='left')# 将t_user表和df_dog根据id进行左连接
    # df=pd.read_csv('data.csv')
    # df=pd.read_excel('data.xlsx',engine='openpyxl')
    # ..........................
    return df.to_dict()
image-20240627114312993
image-20240627114312993

如果有朋友需要pandas练习题的可以关注我的公众号,【小羽网安】回复【pandas】

image-20240627121419260
image-20240627121419260
image-20240627121439447
image-20240627121439447
总结

Flask 是一个用Python编写的轻量级Web应用框架,以其简洁和灵活性而著称,非常适合开发小型至中型的Web应用。以下是对Flask框架的全面总结:

基本特点
  • 轻量级:Flask核心简单,但支持丰富的扩展来构建复杂应用。
  • 灵活性:开发者可以根据项目需求选择所需组件。
  • RESTful支持:易于创建符合REST风格的接口。
  • 模板引擎:内建支持Jinja2模板。
  • 安全性:支持安全cookie和会话管理。
安装与启动
  • 安装命令:pip install Flask
  • 创建应用:定义视图函数并使用@app.route()装饰器映射URL。
  • 运行应用:使用flask runapp.run()命令启动服务器。
路由
  • 使用@app.route()装饰器定义路由。
  • 支持变量规则和多种HTTP方法。
模板渲染
  • 使用render_template()函数渲染HTML模板。
请求和响应
  • 通过request对象访问请求数据。
  • 视图函数返回值自动转换为响应对象。
错误处理
  • 注册错误处理器以自定义错误页面。
会话管理
  • 使用session对象跨请求存储信息。
蓝图(Blueprint)
  • 使用蓝图实现应用的模块化。
静态文件
  • 通过url_for('static', filename='...')提供静态文件。
JSON支持
  • 内建支持JSON数据的序列化和反序列化。
安全性
  • 自动转义HTML以防止XSS攻击。
  • 使用escape()函数手动转义用户输入。
部署
  • 可以部署在外部服务器上,使用--host=0.0.0.0选项。
调试模式
  • 使用--debug选项启动应用,提供错误调试信息。
CORS跨域请求
  • 通过flask-cors扩展或@cross_origin装饰器处理跨域请求。
日志和错误监控
  • 使用Sentry等工具监控和报告错误。
集成WSGI中间件
  • 通过app.wsgi_app属性集成中间件。
消息闪现
  • 使用flash()get_flashed_messages()在用户会话中显示消息。
测试
  • test_request_context()request_context()帮助模拟请求环境。
文件上传
  • 支持文件上传,需设置enctype="multipart/form-data"
集成数据分析
  • 与Pandas等数据分析库结合,进行数据库和数据文件操作。
总结

Flask是一个功能强大且灵活的Web框架,通过其丰富的扩展和简洁的语法,能够快速开发出从简单到复杂的Web应用。其轻量级的特性和对开发者友好的设计,使得Flask成为Python Web开发中的一个受欢迎的选择。

原文链接:https://mp.weixin.qq.com/s/tGNY0E24Pbd6ug6wgeDrqA

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从0到1,Flask全网最全教学!全文1w字,蓝图、会话、日志、部署等使用Flask搭建中小型企业级项目
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档