TT Lab
开始
学习 学习路径 课程

FastAPI — 类型就是契约

不要把配置拼写错误当作默认值

在 TT Lab 中继续学习

目标

测试严格的配置解析、机密剔除和按应用划分的快照。

为什么重要

把字符串 false 转成 bool,结果变成了 True。生产环境里调试响应被打开了,状态页面还把整个配置字典都输出了出来。环境变量是字符串,所以仅靠类型声明,并不能成为安全的值。读取配置的时间点和公开的范围,同样是应用的契约。

步骤

  1. 在 /root/work/fa-config-lab/service.py 中,parse_bool(value) 只对忽略大小写的 true 或 false 返回 bool。如果带有空白或不是字符串,就是 ValueError。

先做一次准备。已有的文件不会被覆盖。

mkdir -p /root/work/fa-config-lab
test -e /root/work/fa-config-lab/service.py || cp /opt/fixtures/ten_labs/fa-config-lab/service.py /root/work/fa-config-lab/service.py
cd /root/work/fa-config-lab
  1. 在 /root/work/fa-config-lab/service.py 中,parse_port(value) 把只含 ASCII 数字的字符串转换为 int,在 1–65535 范围内时返回。其余都是 ValueError。

  2. 在 /root/work/fa-config-lab/service.py 中,parse_timeout(value) 把字符串转换为 float,只返回大于 0 且不超过 30 的有限值。其余都是 ValueError。

  3. 在 /root/work/fa-config-lab/service.py 中,required_token(env) 在 TOKEN 是字符串,且 strip 之后不为空时,返回 strip 后的值。缺失或为空值都是 ValueError。

  4. 在 /root/work/fa-config-lab/service.py 中,load_settings(env) 是一个字典:service=env 的 SERVICE,缺失时为 'api';debug=parse_bool(DEBUG 缺失时为 'false');port=parse_port(PORT 缺失时为 '8000');timeout=parse_timeout(TIMEOUT 缺失时为 '5');token=required_token。空的 SERVICE 是 ValueError。

  5. 在 /root/work/fa-config-lab/service.py 中,public_settings(settings) 是只含 service 和 debug 的新字典。不要修改原件。

  6. 在 /root/work/fa-config-lab/service.py 中,snapshot(env) 返回 load_settings 的结果。调用之后即使修改 env,返回的配置也不会改变。

  7. 在 /root/work/fa-config-lab/service.py 中,create_app(env) 立即读取 snapshot,如果配置非法,就以 ValueError 使应用创建失败。GET /info 只返回 public_settings。用不同的 env 创建的应用之间不共享配置。

参考

显式解析布尔值

在 /root/work/fa-config-lab/service.py 中,parse_bool(value) 只对忽略大小写的 true 或 false 返回 bool。如果带有空白或不是字符串,就是 ValueError。

先做一次准备。已有的文件不会被覆盖。

mkdir -p /root/work/fa-config-lab
test -e /root/work/fa-config-lab/service.py || cp /opt/fixtures/ten_labs/fa-config-lab/service.py /root/work/fa-config-lab/service.py
cd /root/work/fa-config-lab

bool('false') 是 True。请直接比较允许的两个字符串。

保存后用 bash /opt/lab/checks/fa-config-lab/01-contract.sh 确认。

检查端口范围

在 /root/work/fa-config-lab/service.py 中,parse_port(value) 把只含 ASCII 数字的字符串转换为 int,在 1–65535 范围内时返回。其余都是 ValueError。

整数转换成功,并不意味着它是有效的端口范围。

保存后用 bash /opt/lab/checks/fa-config-lab/02-contract.sh 确认。

让超时时间成为有限值

在 /root/work/fa-config-lab/service.py 中,parse_timeout(value) 把字符串转换为 float,只返回大于 0 且不超过 30 的有限值。其余都是 ValueError。

NaN 在一般比较中的表现与预期不同,所以要检查 isfinite。

保存后用 bash /opt/lab/checks/fa-config-lab/03-contract.sh 确认。

拒绝缺失的必需机密

在 /root/work/fa-config-lab/service.py 中,required_token(env) 在 TOKEN 是字符串,且 strip 之后不为空时,返回 strip 后的值。缺失或为空值都是 ValueError。

不要用示例中的默认值来替代缺失的必需机密。

保存后用 bash /opt/lab/checks/fa-config-lab/04-contract.sh 确认。

只对缺失的情形应用默认值

在 /root/work/fa-config-lab/service.py 中,load_settings(env) 是一个字典:service=env 的 SERVICE,缺失时为 'api';debug=parse_bool(DEBUG 缺失时为 'false');port=parse_port(PORT 缺失时为 '8000');timeout=parse_timeout(TIMEOUT 缺失时为 '5');token=required_token。空的 SERVICE 是 ValueError。

get 的默认值与“值 or 默认值”,在处理空字符串时是不同的。

保存后用 bash /opt/lab/checks/fa-config-lab/05-contract.sh 确认。

从公开配置中剔除机密

在 /root/work/fa-config-lab/service.py 中,public_settings(settings) 是只含 service 和 debug 的新字典。不要修改原件。

与对令牌值的一部分做掩码相比,更应采用根本不公开该字段的契约。

保存后用 bash /opt/lab/checks/fa-config-lab/06-contract.sh 确认。

把外部变更与配置分开

在 /root/work/fa-config-lab/service.py 中,snapshot(env) 返回 load_settings 的结果。调用之后即使修改 env,返回的配置也不会改变。

把应用启动时刻的配置,与以后可能变化的输入字典分开。

保存后用 bash /opt/lab/checks/fa-config-lab/07-contract.sh 确认。

确认启动失败和公开响应

在 /root/work/fa-config-lab/service.py 中,create_app(env) 立即读取 snapshot,如果配置非法,就以 ValueError 使应用创建失败。GET /info 只返回 public_settings。用不同的 env 创建的应用之间不共享配置。

为了不让配置错误等到服务器启动后的第一个请求才暴露,要在创建时就验证。

保存后用 bash /opt/lab/checks/fa-config-lab/08-contract.sh 确认。