【Flask】Web启程与实现一个基于Flask的最小应用程序

背景

虽然是一名算法工程师,但是实际情况是不仅要懂算法,还要懂工程。算法实现后,算法效果展示,算法落地等就需要一些工程化的内容。算法工程化的一个简单方式就是将落地的算法包装成服务,供他人调用。
那么问题来了,我们需要了解一下服务相关的内容。对外提供服务的方式也有多种方式,例如基于GRPC的服务,基于Restful的api接口服务等。当然,我个人感觉使用基于restful的api接口方式是使用比较多的。于是在了解Python web的相关框架,如Django,Flask, FastAPI等,综合考虑使用Flask去做这项工作。
那么学习一项技术最好的方式是看官方文档,但是官方文档很多内容是接口,方法的使用,枯燥、无味。这里我就通常看有没有Quickstart以及Tutorial,因为这个一方面是主流方法使用的概括。一方面是一个小demo的实现,读起来能够大致了解一项技术包含哪些内容,另一方面可以了解如果使用该技术去完成一些任务。至于更详细的内容,则可以通过查阅文档进一步挖掘知识。
查看Flask文档,恰好有这两部分内容。于是我就开心的学习和记录相关内容了。当然我也不会一口吞一个胖子,很快的阅读完,一段时间学一点记录一点,毕竟自己的主要工作是算法方面的拓展和实践。
请添加图片描述
当然,后面的记录既是官方文档的翻译也是自己的总结和笔记,献丑啦。

环境准备

flask如何安装可以参考官方网站,本次学习记录主要使用的是2.1.x版本。

为了更好地接近实际工作中的开发方式,我在虚拟机上装了一个centos7系统,并配置了anaconda开发环境。按照flask 2.1.x版本要求Python的版本是3.7+,我也创建了一个python3.7的空开发环境,以便后续可以导出requirements.txt文件。我们可以按照使用venv模块去创建虚拟环境。不过还是比较习惯使用conda去创建虚拟环境,如下:

conda create -n web_flask python=3.7

然后就是在这个环境中安装flask:pip install flask==2.1

下面就是使用pycharm以远程连接的方式连接这个虚拟开发环境了。你可能会想,在自己的虚拟机上开发会不会性能有影响?我觉得这个大可不必担心,这种开发不会吃电脑太多性能。

一个最小应用程序

一个最小的Flask应用可以如下所示:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

上面代码对应的功能如下:

  1. 导入的Flask类,他是WSGI应用程序的一个实例。如果不清楚这个,可以参考百度词条:wsgi
  2. 接下来就是对Flask进行实例化。其中第一个参数就是这个应用的模块或者包的名称。__name__在python中的作用是:第一种情况指的是当前运行的模块,那么当前模块__name__的值就为__main__;第二种情况指的是该模块是使用import导入的模块,那么这个被导入模块的__name__变量的值为该模块的文件名。这是必需的操作,以便 Flask 知道在哪里寻找资源,例如模板和静态文件。
  3. 当我们使用route这个装饰器时,就是告诉Flask去这个方法处理什么样的URL链接。
  4. 该函数返回我们想要在用户浏览器中显示的消息。默认内容类型为 HTML,因此字符串中的 HTML 将由浏览器呈现。

将上面的内容保存到hello.py这个文件中或者使用其他文件名。不过需要确保不是flask.py这个名字,否则会与Flask冲突。

我们使用flask这个命令或者python -m flask去运行这个应用。我们在linux系统hello.py对应文件路径下开始启动他,当然在此之前需要通过导出 FLASK_APP 环境变量来告诉终端要使用的应用程序:

$ export FLASK_APP=hello
$ flask run

于是就有了:
在这里插入图片描述
现在是运行起来了,但是只有服务器那台机器可以访问。

应用发现行为:其实flask对于应用启动是有快捷方式的,当服务程序是app.py或者wsgi.py时,就不需要设置FLASK_APP环境变量的。

程序运行起来后,本质上来说是启动了一个非常简单的内置服务器,其应对测试是没有问题的,但是不适合生产中使用。当系统中有其他程序占用了flask的默认端口5000时,启用是会出现OSError:[Errno98]OSError:[WinError 10013]的错误,这时候则需要更改一下这个程序监听的端口。可以解决的一种方式是在运行命令中添加--port 其他端口号.

当前程序还只能被当前服务器自身访问 ,为了使程序成为一个Extenally Visible Server,可以做的一种方式是在运行开始时添加--host=0.0.0.0,如:flask run --host=0.0.0.0。这就告诉操作系统去监控所有的公网IP了。

根据以上两个问题,汇总开启程序如下:flask run --port 5001 --host=0.0.0.0
在这里插入图片描述
在宿主机访问如下:
在这里插入图片描述
此外,如果宿主机不能访问的话,可能是虚拟机的系统防火墙的问题,需要打开对5000端口的监听。打开方式可以参见【Centos7】防火墙(firewall)常用命令总结.

Debug模式

flask run命令还可以开启调试模式。通过开启调试模式,如果代码发生变动服务器可以自动加载,并且如果请求期间发生错误,则在浏览器中显示交互式调试器。
在这里插入图片描述
需要注意的是:degugger虽然好用,但是最好不要在生产环境中使用,可能会泄露系统的信息。
启动方式:

export FLASK_ENV=development
flask run

HTML转义

flask默认返回的response(响应)是HTML,当然也可以返回json格式的数据等。当返回的内容是HTML时,这时就需要考虑一个问题:injection attacks,flask页面端使用的html渲染引擎是Jinja,将会自动进行防注入攻击处理,就是进行转义。

下面是手动调用的防注入攻击的方式:escape()。为了描述简单,后面的内容都省略了,但是我们需要知道的是,我们是处理的数据都是不可信的数据。

from markupsafe import escape

@app.route("/<name>")
def hello(name):
    return f"Hello, {escape(name)}!"

例如,用户提交的name数据是<script>alert("bad")</script>,那么escape就会将其进行转义,页面端就不会执行这行代码。

可能<name>这种使用方式你还不理解,其就是从URL中获取参数便于服务端根据不同情况处理不同的问题,后面会介绍。

路由Routing

现代 Web 应用程序使用有意义的 URL 来帮助用户。如果页面使用有意义的 URL,他们可以记住并用于直接访问页面,用户更有可能喜欢页面并返回。

每个URL都代表一定意义,根据这些URL指定的意义返回页面是很重要的。于是在flask中,会使用route()这个小东西去绑定一个函数和URL请求。例如:

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello, World'

其实有了这,我们可以做更多!可以使部分 URL 动态化并将多个规则附加到一个函数。因为前文我们已经看到了可以获取URL中的参数。已经有点Restful接口的意思啦。

可变规则

那么如何动态地搞事情呢?我们可以通过使用 <variable_name> 标记部分来将可变部分添加到 URL中。我们的处理函数接受这个 <variable_name> ,就可以作为函数的参数。除此之外,我们可以使用转换器来指定参数的类型,例如 <converter:variable_name>。使用案例如下:

from markupsafe import escape

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return f'User {escape(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 f'Post {post_id}'

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return f'Subpath {escape(subpath)}'

转换参数类型的功能,在低版本的flask是不支持的哦。那么类型转换有哪些呢?如下:

string(默认值)接受任何不带斜线的文本
int接受正整数
float接受正浮点值
path像字符串,但也接受斜杠
uuid接受 UUID 字符串

独特的URLS/重定向行为

以下两条规则在使用斜杠时有所不同。

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

其中,projects这个URL的尾部有一个斜线"/",这似乎更像一个文件系统中的文件夹。如果您访问的 URL 不带斜杠 (/projects),Flask 会将您重定向到带有斜杠 (/projects/) 的规范 URL。

about 端点的规范 URL 没有尾部斜杠。它类似于文件的路径名。使用尾部斜杠 (/about/) 访问 URL 会产生 404“未找到”错误。这有助于使这些资源的 URL 保持唯一性,从而帮助搜索引擎避免将同一页面编入两次索引。

也就是说,程序中URL中加上斜杠,用户访问的URL没有斜杠时,系统会补全。而程序中URL不加斜杠,用户访问的URL有斜杠时,那么就会报错。

URL构建

要构建特定函数的 URL,可以使用url_for()这个函数。它接受函数的名称作为其第一个参数和任意数量的关键字参数,每个关键字参数对应于 URL 规则的可变部分。未知的可变部分作为查询参数附加到 URL。

那么为什么要使用 URL 反转函数url_for()构建 URL,而不是将它们硬编码到模板中?

  1. 反转通常比对 URL 进行硬编码更具描述性。
  2. 您可以一次性更改 URL,而无需记住手动更改硬编码的 URL。
  3. URL 构建透明地处理特殊字符的转义。
  4. 生成的路径总是绝对的,避免了浏览器中相对路径的意外行为。
  5. 如果您的应用程序位于 URL 根之外,例如,在 /myapplication 而不是 / 中,则 url_for() 会为您正确处理。

例如,这里我们使用test_request_context()方法来尝试 url_for()test_request_context() 告诉 Flask 表现得好像它正在处理一个请求。

from flask import url_for

@app.route('/')
def index():
    return 'index'

@app.route('/login')
def login():
    return 'login'

@app.route('/user/<username>')
def profile(username):
    return f'{username}\'s profile'

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

HTTP协议中方法

Web 应用程序在访问 URL 时使用不同的 HTTP 方法。在使用 Flask 时,您应该熟悉 HTTP 方法。默认情况下,路由只响应 GET 请求。您可以使用 route() 装饰器的方法参数来处理不同的 HTTP 方法。

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()

上面的示例将路由的所有方法保留在一个函数中,如果每个部分都使用一些公共数据,这将很有用。

您还可以将不同方法的视图分成不同的功能。 Flask 为每种常见的 HTTP 方法提供了一种使用 get()post() 等装饰此类路由的快捷方式。

@app.get('/login')
def login_get():
    return show_the_login_form()

@app.post('/login')
def login_post():
    return do_the_login()

如果 GET 存在,Flask 会自动添加对 HEAD 方法的支持并根据 HTTP RFC 处理 HEAD 请求。同样,OPTIONS 会自动为您实施。

总结

码的有点累,下次周末继续搞一波。quickstart不知不觉已过半,加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智绘山河

你的鼓励可能解决你下一个问题

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值