Python测开28期 - 旎奥 - 学习笔记 - 后端开发(一)

一、Flask 环境安装

要使用 Flask 框架,需要先通过命令行 pip 工具安装 Flask。

pip install flask

以下是一个简单的 Flask 入门示例,演示了如何创建一个基本的 Web 应用程序:

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)

# 定义路由和视图函数
@app.route("/")
def hello():
    return "Hello Flask!"

上述示例代码创建了一个最简单的 Flask 应用程序。

  1. 导入 Flask 模块:首先,我们需要导入 Flask 模块,以便使用 Flask 框架提供的功能。
  2. 创建应用程序实例:通过实例化 Flask 类并传入 __name__ 参数,我们创建了一个 Flask 应用程序的实例。__name__ 是一个特殊变量,它表示当前模块的名称。
  3. 定义路由和视图函数:使用 @app.route() 装饰器来定义路由,指定 URL 路径和对应的视图函数。在上面的示例中,我们定义了根路径 '/' 的视图函数 hello()
  4. 视图函数:视图函数是处理请求并返回响应的函数。在上面的示例中,hello() 函数返回字符串 'Hello, Flask!',它将作为浏览器中访问根路径时的响应。

Flask 运行使用

  • 代码调用
    • app.run()
  • 命令行运行
    • bash(mac/linux)
    • cmd(windows)
    • powershell(windows)

在代码中可以通过使用 app.run() 方法运行应用程序。默认情况下,应用程序会运行在本地服务器上,监听 5000 端口。

# 运行应用程序
if __name__ == '__main__':
    app.run()

可以通过访问 http://localhost:5000/ 在浏览器中查看结果。

或者也可以在命令行中启动应用程序。Flask 框架自带了一个运行工具叫做flask run,可以在项目目录下使用命令行命令flask run来启动 Flask 项目。这个命令会启动一个开发服务器,并在浏览器中打开项目页面。

启动前可以通过设定环境变量 FLASK_APP=文件名 来表示将要运行的应用是哪个,然后运行 flask run 来启动该应用。

不同的操作系统设置环境变量的命令是不同的,具体参考以下命令:

# linux/mac: 命令行运行
$ export FLASK_APP=hello
$ flask run

# windows: 命令运行
> set FLASK_APP=hello
> flask run

二、接口路由技术

什么是路由

路由是将 URL 地址与应用程序中的函数相映射的过程。当用户在浏览器中输入特定的 URL 地址时,Flask 会调用与该地址相匹配的函数并返回相应的结果。

路由的应用场景

在 Web 应用程序都通过路由技术使用 URL 链接来控制网页显示的内容,只要知道 URL 链接,即使没有主页导航也可以直接访问所需网页。

通过 Flask 实现路由

路由类型 实现方式
基本路由 @app.route('/')
动态路由 @app.route('/user/<username>')
限定类型 @app.route('/post/<int:post_id>')

在 Flask 中,可以使用 @app.route() 装饰器来定义路由。路由决定了用户请求的 URL 路径与对应的视图函数之间的关系。

基本路由

基本路由定义方式为:

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)

# 定义基本路由
@app.route("/")
def index():
    return "Home Page"

@app.route("/about")
def about():
    return "About Page"

# 运行应用程序
if __name__ == '__main__':
    app.run()

在上述示例中,@app.route('/')@app.route('/about') 分别定义了根路径 '/'/about 的路由。当用户访问这些路径时,Flask 会调用对应的视图函数并返回相应的内容。

@app.route('/') 访问地址为 http://127.0.0.1:5000/@app.route('/about') 访问地址为 http://127.0.0.1:5000/about

动态路由

URL 中某些地方存在可变部分,为动态的 URL,Flask 支持这种动态 URL。

动态路由是通过尖括号 <> 实现的,放在尖括号里面的部分为动态部分,即在装饰器 route 传入 <> 部分,定义视图函数时,将该 <> 里面的内容作为参数传入到函数里面即可。

比如路由 /user/Harry 后面想要根据不同的用户名,返回不同的数据。这种情况可以用变量来代替名字部分:@app.route('/user/<username>')

这时 username 传入不同的值时,比如依次传入 Harry、Ron、Hermione,那么对应的路由就变为:/user/Harry/user/Ron/user/Hermione

# 定义动态路由
@app.route("/user/<username>")
def user_info(username):
    return f"User {username} is select info."

访问:http://127.0.0.1:5000/user/Ron

查看服务端日志,会有一条返回信息:User Ron is select info.

限定类型

如果希望限定输入的动态字段的类型,可以使用 <类型:变量名> 来实现,例如如果希望某个字段只能是整数,那么可以写为 /user/<int:user_id>

# 限定类型的动态路由
# 类型限定为整型
@app.route("/user/<int:user_id>")
def user_id(user_id):
    # 展示给定的用户 ID,ID 为整型
    return f"User ID is {user_id}"

# 类型限定为 path(可以包含 /)
@app.route('/path/<path:sub_path>')
def show_subpath(sub_path):
    # 展示 path 后的子路由
    return f'Subpath is {sub_path}'

# 动态路由
@app.route("/userinfo/<string:username>/")
def hogw(username):
    logger.info(f"这是{username}同学的个人信息")
    return f"这是{username}同学的个人信息"


if __name__ == '__main__':
    # 启动flask服务
    # 轮询等待的方式,等待浏览器发来请求
    # 会一直接受请求,知道程序停止
    app.run()

Flask 支持在路由上使用的类型有 int、float、string、path。path 类型是字符串的一种,不同的是它可以包含正斜线。

路由规则

Flask 的 URL 规则基于 Werkzeug 的路由模块。可以确保形成的 URL 是唯一的,并且基于 Apache 规定的先例。

@app.route('/about')
def about():
    return 'About Page'

@app.route('/hogwarts/')
def hello_hogwarts():
   return 'Hello Hogwarts'

if __name__ == '__main__':
    app.run()

这两个路由定义的例子看起来类似,其实是不一样的。

在第二个例子中,路由的尾部使用斜杠 (/)。因此,它成为一个规范的URL。这时 Flask 会自动进行重定向。当在浏览器的地址栏中不输入 / 时,会自动在尾部加上一个斜杠。也就是在浏览器的地址栏中输入 / 和不输入 /的效果是一样的。

而第一个例子中,路由的尾部没有 /。那么访问的时候,在浏览器的地址栏中不能输入 /,否则会产生 “Not Found” 的错误。这样可以保持 URL 唯一。

三、请求与响应 - 请求方法

接口常用请求方法

Flask 框架支持常见的 HTTP 请求方法,最常用的请求方法为:

请求 说明
GET 获取服务器资源
POST 新增服务器资源
PUT 更新服务器资源(客户端提供改变后的完整资源)
DELETE 删除服务器资源

这些请求方式都是去操作资源的。

  • GET:一般是从 URI 中从 服务器中获取资源(比如获取用例列表,比如,获取用户信息等等),但一般 GET 是存在不安全性的,如果有敏感信息,会使用 POST。
  • POST: 主要用于将【数据发送到服务器】创建或更新资源。注意,POST 对数据长度是没有限制的,GET 会有限制,这是因为某些浏览器对请求的 URL 长度有限制。
  • PUT:也是用于将 【数据发送到服务器】创建或更新资源。
  • DELETE:用来删除指定的资源。

Flask 支持的请求方法通过在路由定义时使用 methods 参数进行指定。可以指定多个请求方法,以列表的形式传递给 methods 参数。

下面分别演示这四种请求的用法。

GET 请求

默认情况下,Flask 路由定义的视图函数只支持 GET 请求。如果需要支持其他请求方法,需要显式地指定 methods 参数。

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)


# get 请求
@app.route("/get")
def get():
    return f"Method is GET."

@app.route("/get_method", methods=["GET"])
def get_method():
    return f"GET method success."

if __name__ == '__main__':
    app.run()

POST 请求

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)


# post 请求
@app.route("/post", methods=["POST"])
def post():
    return f"Method is POST."


if __name__ == '__main__':
    app.run()

PUT 请求

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)


# put 请求
@app.route("/put", methods=["PUT"])
def put():
    return f"Method is PUT."


if __name__ == '__main__':
    app.run()

DELETE 请求

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)


# delete 请求
@app.route("/delete", methods=["DELETE"])
def delete():
    return f"Method is DELETE."


if __name__ == '__main__':
    app.run()

from flask import Flask

app = Flask(__name__)


# methods是一个列表类型,可以添加多种请求方式get post put delete
@app.route("/cases", methods=["get"])
def get_case():
    return {"cede": 0, "msg": "get success"}


@app.route("/cases", methods=["post"])
def post_case():
    return {"cede": 0, "msg": "post success"}


@app.route("/cases", methods=["put"])
def put_case():
    return {"cede": 0, "msg": "put success"}


@app.route("/cases", methods=["delete"])
def delete_case():
    return {"cede": 0, "msg": "delete success"}


if __name__ == '__main__':
    app.run()

image


image

四、请求与响应 - 处理请求数据

request 对象

当浏览器去访问一个地址时,HTTP 协议会向后台传递一个 request 对象。这个 request 对象包含请求头、请求参数、以及请求方式。后台可以取到 request,然后进行逻辑处理。

在 Flask 中,可以使用 request 对象来处理请求数据。request 对象提供了访问请求数据的方法和属性。

request 的常用属性/方法

以下是一些常见的处理请求数据的方法:

属性/方法 说明
args 记录请求中的查询参数
json 记录请求中的 json 数据
files 记录请求上传的文件
form 记录请求中的表单数据
method 记录请求使用的 HTTP 方法
url 记录请求的 URL 地址
host 记录请求的域名
headers 记录请求的头信息

普通请求参数处理

如果一个 GET 请求在 URL 中拼接了请求参数,可以使用 request.args 来获取 GET 请求中携带的请求参数。request.args 是一个字典,可以通过键名来获取参数的值。

from flask import Flask, request

# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/user')
def get_user():
    # 获取 URL 中的请求参数
    url_param = request.args
    # 查看获取到的请求参数的类型
    print(type(url_param))
    # 获取请求参数中的 username 对应的值
    username = url_param.get('username')
    return f'Hello, {username}!'


# 运行应用程序
if __name__ == '__main__':
    app.run()

访问 /user?username=John,将返回 'Hello, John!'

查看服务端日志信息,可以看到 request.args 取到的值的数据类型为 werkzeug.datastructures.structures.ImmutableMultiDict,也是一个可迭代的对象,访问方法和基础的字典类似。想要获取这个字典中 username 对应的值,可以直接使用 get() 方法。

@app.route("/login/", methods=["get"])
def login():
    logger.info(f"请求参数为:{request.args}")
    result = request.args
    a = result.get("a")
    b = result.get("b")
    print(f"a={a}, b={b}")
    return {"code": 0, "msg": "get success"}

JSON 请求体处理

如果是 POST 或者 PUT 相关的请求,需要带有 JSON 数据格式,可以使用 request.json 来获取 POST 请求中的 JSON 数据。request.json 会解析请求的 JSON 数据,并返回一个包含解析后的数据的字典,然后可以根据需要获取特定的字段值。

from flask import Flask, request

# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/data', methods=['POST'])
def process_data():
    # 获取 JSON 格式请求体
    data = request.json
    # 查看获取到的请求参数的类型
    print(type(data))
    # 获取请求体中对应字段的值
    name = data.get('name')
    age = data.get('age')
    return f'Name: {name}, Age: {age}'


# 运行应用程序
if __name__ == '__main__':
    app.run()

提交 JSON 格式请求体,请求体中包含姓名和年龄,将返回姓名和年龄的信息。

@app.route("/regist", methods=["post"])
def post_regist():
    logger.info(request.json)
    return {"code": 0, "msg": "post success"}

表单请求体处理

如果是 POST 相关的请求,需要带有 form 表单格式数据格式,可以使用 request.form 来获取 POST 请求中的表单数据。request.form 是一个字典,可以通过键名来获取表单字段的值。

from flask import Flask, request

# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/login', methods=['POST'])
def login():
    # 获取表单格式请求体
    user_info = request.form
    # 查看获取到的请求参数的类型
    print(type(user_info))
    # 获取请求体中对应字段的值
    username = user_info.get('username')
    password = user_info.get('password')
    return f'Welcome, {username}!'


# 运行应用程序
if __name__ == '__main__':
    app.run()

通过提交包含用户名和密码的表单,将返回欢迎信息。


# 表单接口,注册:用户名、密码、确定密码、邮箱
@app.route("/regist1", methods=["put"])
def put_regist():
    name = request.form.get("name")
    password = request.form.get("password")
    password_confirm = request.form.get("password_confirm")
    email = request.form.get("email")
    logger.info(request.form)
    logger.info(f"注册的用户信息为:name:{name}, password:{password}, password_confirm:{password_confirm}, email:{email}")
    return {"code": 0, "msg": "put success"}

文件请求体处理

如果页面上需要提交一个图片,或者上传一个文件到后端服务器,可以使用 request.files 来获取请求中包含的文件。request.files 是一个字典,每个上传的文件都会存储在这个字典里。可以通过 file 这个 key 来获取其中的文件对象。

已上传的文件存储在内存或是文件系统中一个临时的位置。它有一个 save() 方法,这个方法允许把文件保存到服务器的文件系统上。

如果想知道上传前文件在客户端的文件名是什么,可以访问 filename 属性。但这个值是可以伪造的。如果要把文件按客户端提供的文件名存储在服务器上,需要把它传递给 Werkzeug 提供的 secure_filename() 函数。这个函数可以用来确保文件名是安全的。

from flask import request
from werkzeug import secure_filename


# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/upload', methods=['POST'])
def upload_file():
    # 获取文件格式请求体
    file = request.files
    # 查看获取到的请求参数的类型
    print(type(file))
    # 获取请求体中对应字段的值
    f = file.get("file")
    # 保存文件
    # 需要提前创建 uploads 目录,secure_filename 可以确保文件名安全
    f.save('./uploads/' + secure_filename(f.filename))
    return f'File {f.filename} is saved!'


# 运行应用程序
if __name__ == '__main__':
    app.run()

# 处理客户端发来的文件请求
@app.route("/file/", methods=["post"])
def post_file():
    file_obj = request.files.get("file")
    logger.info(file_obj)
    file_name = file_obj.filename
    logger.info(f"文件名为:{file_name}")
    file_obj.save("./logo1.png")
    return {"code": 0, "msg": "post success"}
import requests


def test_file():
    url = "http://127.0.0.1:5000/file"
    file = {"file": open("/Users/mei/Downloads/timg.jpeg", "rb")}
    r = requests.post(url, files=file)
    assert r.status_code == 200

通过提交包含文件的请求体,将返回文件保存息。


其他请求参数处理

如果在进行业务处理的时候,需要根据不同的请求 URL、域名、请求参数或者请求头信息进行不同场景的判断,则可以使用以下方法获取对应的值。

from flask import request
from werkzeug import secure_filename


# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    # 获取请求 URL
    r_url = request.url
    # 获取请求域名
    r_host = request.host
    # 获取请求头信息
    r_headers = request.headers
    # 获取请求方法
    r_method = request.method
    print(r_url, r_host, r_headers, r_method)
    # 获取文件请求体
    r_file = request.files
    # 判断请求方法为 POST
    if r_method == 'POST':
        # 判断请求头中包含 My-Header 字段并且值为 hogwarts
        if r_headers.get('My-Header') == "hogwarts":
            # 保存文件
            f = r_file.get("file")
            f.save('./uploads/' + secure_filename(f.filename))
            return f'File {f.filename} is saved! URL is {r_url}, host is {r_host}'
        return f"My-Header is missing!"
    return f"Method is wrong!"


# 运行应用程序
if __name__ == '__main__':
    app.run()

这样就可以校验请求方法、请求头信息,然后再完成对应的业务逻辑处理了。

五、请求与响应 - 处理响应信息

针对于后端服务,需要针对请求返回对应的响应信息。

响应信息可以响应很多类型的信息类型。常见的比如文本类型,还有非常通用的 JSON 数据。

在 Flask 中,可以使用视图函数来处理接口请求并生成相应的响应。而且 Flask 提供了多种方式来构建和返回接口响应。

接口响应常见类型

  • 文本型
  • 元组
  • JSON
  • HTML
  • 额外数据

返回文本类型

首先来看最简单的类型,返回一个文本数据。

比如现在编写一个接口,设定路由为 /text,请求方法为默认的 GET 请求。返回一串文本信息。

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)


# 定义路由和视图函数
@app.route('/text')
def text_res():
    return '返回文本'


# 运行应用程序
if __name__ == '__main__':
    app.run()

示例中,视图函数 text_res() 直接返回一个字符串 '返回文本',Flask 将自动将其封装成一个纯文本响应并发送给客户端。打开浏览器,就可以看到接口返回的文本内容。

此外,打开浏览器开发者工具 Network 面板查看这个接口,可以注意到 Response Headers 中的Content-Typetext/html; charset=utf-8

返回元组类型

除了文本,接口还可以返回元组格式。

元组格式包含 3 个参数类型。第一个是 response 对象,第二个是响应状态码,第三个是响应头信息。也可以只填写 2 个返回信息。比如 (response, status) 结合,还有 (response, headers) 结合。

例如:如果 3 个返回数据都添加响应信息。

from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/tuple')
def tuple_res():
    return "你好呀", 200, {"hogwarts": "Harry"}

# 运行应用程序
if __name__ == '__main__':
    app.run()

可以看到响应状态码为 200,响应头信息中包含了 Hogwarts 这个字段。

返回 JSON

在前后端分离开发的实践中,后端提供的接口响应信息基本都为通用的 JSON 格式。

返回 JSON 类型的响应数据有两种方法。

  1. 第一种是使用 jsonify() 方法,此方法支持,直接传入一个字典,也支持通过关键字参数传递。
# 返回json
from flask import Flask, jsonify

# 创建 Flask 应用程序实例
app = Flask(__name__)


@app.route('/json')
def get_json():
    # jsonify({'status': 0})
    return jsonify(status=1, name="lily", age=20)

# 运行应用程序
if __name__ == '__main__':
    app.run()
  1. 第二种方法就是直接返回字典,在 Flask 1.1 版本之后,直接返回 python 的字典类型时,Flask 会调用 jsonify() 方法。
# 返回字典
from flask import Flask

# 创建 Flask 应用程序实例
app = Flask(__name__)

# 定义路由和视图函数
@app.route('/dict')
def get_dict():
    return {'status': 0}


# 运行应用程序
if __name__ == '__main__':
    app.run()

以上 /json/dict 路由的响应信息的 Content-Type 均为 application/json

返回 HTML

接口还可以直接返回 HTML。返回 HTML 主要使用的是模板渲染技术。

from flask import Flask, render_template

# 创建 Flask 应用程序实例
app = Flask(__name__)

@app.route('/html')
def get_html():
    # 调用render_template方法,传入html 文件的名称。
    # 注意html文件必须在 templates 目录下
    return render_template('demo.html')

# 运行应用程序
if __name__ == '__main__':
    app.run()

可以在 templates 目录下新建 demo.html 文件。

<!-- 
html文件必须在templates目录下
/application.py
/templates
    /demo.html 
    -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Hogwarts</title>
</head>
<body>
    <h1>霍格沃兹测试开发</h1>
</body>
</html>

浏览器中访问 /html 路由,发现页面中展示了 霍格沃兹测试开发

返回额外数据

在构造响应的过程中,除了构造响应体,响应头,响应状态码等,可能还要添加其他的响应信息。比如需要添加 cookie,则可以使用 make_response() 方法。

from flask import Flask, render_template, make_response

# 创建 Flask 应用程序实例
app = Flask(__name__)

# 定义路由和视图函数
@app.route('/')
def index():
    resp = make_response(render_template('demo.html'))
    # 设置cookie
    resp.set_cookie('username', 'the username')
    # 设置响应头信息
    resp.headers["hogwarts"] = "Hary"
    return resp


# 运行应用程序
if __name__ == '__main__':
    app.run()

浏览器中访问 http://127.0.0.1:5000 路由,发现页面中展示 霍格沃兹测试开发。且响应头信息中包含 Hogwarts 字段,值为 Hary。Set-Cookie 中包含设置的 Cookie 信息。

六、测试平台环境配置

在 Flask 中,可以使用 app.run() 方法来运行应用程序并设置要监听的主机和端口。

app.run() 方法默认会监听在默认的主机地址 '127.0.0.1'(或 'localhost')和端口 5000 上。可以通过访问 http://localhost:5000/ 来访问应用程序。

监听主机

  • app.run() 设置 host 参数指定要监听的主机地址:
    • 127.0.0.1 只能本机访问。
    • 0.0.0.0 服务发布到局域网。
app.run(host="0.0.0.0")

当 host 值为 '0.0.0.0',这表示应用程序将监听所有可用的网络接口。可以通过访问 http://<your-ip> 来访问应用程序,其中 <your-ip> 是运行应用程序的主机的 IP 地址。

监听端口

  • app.run() 设置 port 参数(默认端口号 5000)
app.run(host="0.0.0.0", port=5050)

app.run() 使用 port 参数指定要监听的端口。默认端口为 5000,可以改为其他需要的端口。

Debug 模式

在 Flask 中,可以通过设置调试模式(Debug Mode)来启用或禁用调试功能。调试模式是开发阶段常用的工具,它提供了一些有用的功能,以帮助开发者进行调试和错误排查。

  • app.run() 设置 debug=True(默认是 production)

调试模式的作用

  1. 显示详细的错误信息:在调试模式下,当应用程序出现错误时,Flask 会显示详细的错误信息,包括错误堆栈跟踪。这对于定位和修复错误非常有帮助。
  2. 自动重新加载代码:调试模式下,如果你修改了应用程序的代码文件,Flask 会自动重新加载修改后的代码,而无需手动重启应用程序。这样可以加快开发的迭代速度,节省重启服务器的时间。
  3. 支持实时调试器:调试模式下,Flask 提供了一个实时调试器(Debugger),可以在浏览器中显示源代码和调试信息,并允许你在运行时进行断点调试和变量查看。
app.run(host="0.0.0.0", port=5050, debug=True)

要注意的是,在生产环境中,不建议使用调试模式,因为它会暴露应用程序的详细信息,并且自动重新加载代码可能会导致安全风险。调试模式应仅在开发阶段使用,并且在部署到生产环境之前,务必禁用调试模式。

因此,调试模式是一个方便的开发工具,它提供了错误信息的显示、自动重新加载代码和实时调试器等功能,可以加速开发过程和调试错误。但在生产环境中应该禁用调试模式以确保应用程序的安全性和性能。