06.28.每日总结

📅 2026/6/30 22:08:02
06.28.每日总结
今天好兄弟来了 所以有小半天时间基本没做事剩余时间也都是在反思web开发和模拟项目上了下面为今日进度农民工项目进度今天实际进度没有多少主要是学了些web开发里的常识和网课没讲的内容登录功能和查询功能完成 添加工人功能写了一半添加了一些关键功能比如数据校验 自定义异常管理器 网页程序关闭学习了校验模型 枚举类 3XX重定向主要还是排查了一些代码里可能存在的错误6月28日周日fastapi模拟项目与千问的问答总结问如果用户什么都不输入点击登录相当于表单没有传任何参数这时候没有跳转到登录失败界面而是网页失效答当表单什么都不填直接点击登录时前端发送了空请求后端在处理时比如尝试用空用户名去字典里查找或者进行字符串操作抛出了 Python 异常。因为你的后端没有“捕获”这个异常FastAPI 就会默认返回一个 500 Internal Server Error这就导致了你看到的“网页失效”。要解决这个问题我们需要在 FastAPI 后端增加参数校验和异常处理。数据的参数校验问如何进行参数校验答FastAPI 深度集成了 Pydantic。你可以定义一个数据模型要求 username 和 password 不能为空。如果前端传了空值FastAPI 会自动拦截并返回标准的 422 Unprocessable Entity 错误而不会导致网页崩溃。from fastapi import FastAPI, Form, HTTPExceptionfrom pydantic import BaseModelapp FastAPI()class LoginData(BaseModel):username: strpassword: strapp.post(/login) async def login(data: LoginData Form()):pass除了后端处理强烈建议在你的 HTML 登录表单的input标签中加上 required 属性。这样如果用户什么都不填浏览器会直接弹出提示框根本不会把请求发给后端。问是不是可以这样理解 对于所有需要前端传回数据䣌场合 最好都使用参数校验 添加校验模型答完全正确 在 FastAPI以及绝大多数现代后端框架的开发中“永远不要信任前端传来的数据” 是一条铁律。对于所有需要前端传回数据的场合使用 Pydantic 模型进行参数校验不仅是最佳实践更是保障后端安全、稳定的核心手段。补充如何处理数据校验失败的提示默认情况下如果 Pydantic 校验失败FastAPI 会返回一个包含 loc、msg、type 的 JSON 数组这对于前端来说可能不够直观。如果你希望返回更友好的中文提示可以在模型中使用 Fieldfrom pydantic import BaseModel, Fieldclass LoginData(BaseModel):username: str Field(..., min_length1, description用户名不能为空)password: str Field(..., min_length6, description密码长度不能少于6位)使用数据校验类的过程中我发现一个问题啊 如果这个数据本来是字符串它一经过数据校验类之后类型就变成数据校验类本身了这意味着它以前的字符串方法就全部不能用了这不太好吧思想Pydantic 模型就像一个“海关安检机”。字符串进去经过安检类型转换、格式校验、清洗出来的时候被包装成了一个“带有护照对象属性的旅客”。它确实不能直接当字符串用了但它换来了绝对的安全和结构的清晰。那么该如何查看原来数据的属性“把它的护照给它拿掉”Pydantic对象里的属性依然是那个纯正的字符串。你只需要通过 .属性名 把它“拿”出来就可以继续使用所有字符串方法了。问如果我在我的校验模型里写入 worker_age:int 如果传输来的数据不能顺利转换成int就会被校验模型阻止吗答正确。当你在 Pydantic 模型中定义 worker_age: int 时FastAPI 会在数据进入你的业务代码之前自动执行一道“安检”程序。如果失败FastAPI 根本不会执行你写的业务代码而是直接给前端返回一个 HTTP 422 Unprocessable Entity无法处理的实体错误。问那如果我在模型校验里写入联合类型Union[int,str] 这时候数据传过来又会执行怎么样的操作呢Pydantic 会执行“逐个尝试成功即止”的宽容策略。在 Pydantic 的 Union 机制中类型的排列顺序直接决定了校验的优先级。你提到的“把不好通过的类型更严格的类型写在前面”正是实际开发中处理联合类型的黄金法则。补充数据校验的pydanticPydantic的Field函数简直就是一个“万能安检机”管字符串长度用 min_length 和 max_length管数值大小用 ge, gt, le, lt管格式用 pattern正则把这些规则全写在模型里你的后端代码不仅绝对安全而且非常清爽异常处理----自定义异常处理器问针对输入空的用户名和密码返回422这个了解了 但是这个页面不好看 我想也链接到登录失败这个页面答1 编写好异常后跳转的网页 2 自定义异常处理器我们需要导入 RequestValidationError这是 FastAPI 校验失败时抛出的异常并告诉 FastAPI一旦遇到这个异常就返回我们的漂亮页面而不是默认的 JSON。from fastapi.exceptions import RequestValidationError#使用 app.exception_handler 装饰器覆盖默认的校验错误处理器app.exception_handler(RequestValidationError)async def validation_exception_handler(request: Request, exc: RequestValidationError):# 提取 Pydantic 报错的具体信息error_messages [err[msg] for err in exc.errors()]friendly_msg .join(error_messages)# 返回自定义的登录失败页面return render_login_fail_page(friendly_msg)问有关HTTPException答语法from fastapi import HTTPExceptionraise HTTPException(status_code400, detail用户名和密码不能为空)状态码3XX重定向问为什么使用status_code303答在 POST 请求发生错误时官方推荐使用 303 See Other 状态码进行重定向。它会强制浏览器使用 GET 方法去请求 /worker_system/register/fail 页面这样可以避免浏览器在刷新页面时重复提交登录表单防止表单重复提交问题。补充以后在使用 RedirectResponse 时请遵循以下“黄金法则”1 表单提交后的页面跳转如登录成功、注册成功必须使用 303----以GET方式查看另一个网页 2 API 之间的内部转发需要保留 POST/PUT 方法和参数使用 307----临时重定向并保持原来的请求方式不变问当其他业务文件用到app时从main.py调用app这样会触发循环调用的报错那我该怎么处理呢答如果你为了代码整洁把登录接口抽离到了 auth.py 文件中这时候才涉及到“导入”的问题。但注意不是 auth.py 导入 main.py 的 app这会引发你之前遇到的循环导入报错而是在 main.py 中导入 auth.py 的路由。问但如果按照上面写法在其他模块写分路由最后加载main文件的app中 会出现以下警告非报错类 ‘APIRouter’ 的未解析的特性引用 ‘exception_handler’答表层为什么会出现这个警告因为 APIRouter 和 FastAPI 是两个不同的类。 FastAPI也就是 app是主应用它自带 app.exception_handler 装饰器。 APIRouter也就是 router是子路由模块它的设计初衷是只负责处理路由和请求逻辑。 你的 IDE 检查到你在 router 对象上调用了 exception_handler发现 APIRouter 类本身并没有定义这个特性所以贴心地给你画了个黄线警告。深层核心原则异常处理器必须写在 main.py 中 在 FastAPI 的模块化架构中全局的异常处理器、中间件Middleware等都必须在主应用 app 上统一注册而不是在子路由 router 中注册。所以正确的做法是 把异常处理代码移到 main.py 中使用 app。 子路由文件 auth.py 保持干净只写业务逻辑。问那即使我想把异常处理器处理后因为异常所以登录不上去的页面也链接到登录失败的页面这个页面是分属于register模块的按照规范我也依然应该把异常处理器写在main里给app用是这个意思吗答完全正确你的理解非常精准。在 FastAPI 的模块化架构中无论你要跳转的目标页面属于哪个模块哪怕它是 register 模块的全局异常处理器都必须写在 main.py 的 app 上。为什么必须这么设计1 全局视野app 是整个应用的大管家而 router 只是负责处理特定路径的“部门经理”。当发生异常如 422 校验错误时这属于全局级别的事件必须由大管家来统一接管和分发。2 避免循环导入如果你把异常处理器写在 register 模块里或者试图在 register 里导入 main 的 app就会触发你之前遇到的 circular import 报错。3 URL 只是字符串对 main.py 来说它不需要知道 /worker_system/register/fail 这个页面是怎么实现的它只需要知道这是一个合法的 URL 字符串。只要你的 main.py 中通过 app.include_router() 把 register 模块挂载进来了这个 URL 就是生效的。补充 FastAPI 默认期望接收 JSON 格式的数据而你的 HTML 表单提交的是 application/x-www-form-urlencoded 格式。当 FastAPI 发现请求头不是 JSON 时它会直接拒绝并抛出 RequestValidationError导致你的异常处理器被触发。 要让 FastAPI 正确接收 HTML 表单数据必须在路由函数的参数上显式加上 Form(...)。问GET和POST到底是什么东西 要保证哪两处的这两种请求一致才能顺利链接简单来说GET 和 POST 是 HTTP 协议中最常用的两种请求方式动作。你可以把它们理解为前端发给后端的“指令”GET获取语义是“只读查询”。用来向服务器要数据比如打开一个网页、查询一个用户信息。它不会修改服务器上的任何数据。br POST提交语义是“写入/修改”。用来向服务器提交数据比如登录、注册、下单。它会触发服务器的业务逻辑可能会改变数据库里的数据。要保证前端的method参数和后端的router.xxx一致才能顺利链接问PRG模式答在 Web 开发中处理完表单提交POST后跳转到新页面业界标准的做法是 PRG 模式Post-Redirect-Get。你需要显式设置 status_code303。它的行为是明确要求客户端使用 GET 方法去访问新地址并且会丢弃原来的请求体表单数据。这不仅能完美解决 405 报错还能防止用户在成功页面点击“刷新”时浏览器弹出“是否重新提交表单”的警告。问我的swagger文档测试正常显示了登录成功的界面但是我实际操作时明明输入同样的用户名和密码就显示method not allowed答原因非常简单Swagger 文档和浏览器直接访问的底层逻辑完全不同。1 Swagger 的测试机制当你在 Swagger 界面点击 Try it out 并输入用户名密码时Swagger 实际上是在后台帮你发送了一个标准的 POST 请求通常还会带上 application/json 的请求头。你的后端完美接收了这个 POST 请求处理成功后返回了 303 重定向Swagger 忠实地把这个结果展示给了你。浏览器直接访问的机制当你在浏览器的地址栏直接输入 URL比如 /worker_system/register/ok并按下回车时浏览器永远只会发送 GET 请求。 如果你的后端代码里这个成功页面只定义了 router.post(/ok)或者因为某种原因 POST 路由没有生效FastAPI 收到 GET 请求后发现“此路不通”就会直接给你返回 405 Method Not Allowed。问那么现在场景是这样的我的后端数据端的性别存储是字典主表字典项表根据1男2女的逻辑来的 那么前端的下拉框男女的value值是1和2可是再想一下 我要是前端的value直接写男和女 后端也省事一点直接存性别是男和女 舍弃掉中间的1和2的两次后端转换不就好了嘛答因为有以下隐患1. 存储空间与查询性能最直观的差距存汉字在数据库中一个汉字UTF-8编码通常需要占用 3个字节。存数字而一个数字如 TINYINT只需要 1个字节。如果你的系统有 100 万条工人数据存汉字需要约 3MB存数字只需 1MB。更重要的是在进行数据过滤、排序时数字比较的速度远快于字符串比较。2. 数据一致性防不胜防的脏数据3. 国际化与多语言扩展未雨绸缪4. 跨系统兼容性关于你说的“后端转换麻烦”的误区 其实“1和2”与“男和女”之间的转换在后端根本不需要你手动写复杂的逻辑既然你使用了 FastAPI Pydantic最优雅的做法是利用枚举Enum。你只需要在后端定义一个枚举类FastAPI 就会自动帮你完成转换from enum import IntEnumclass GenderEnum(IntEnum):MALE 1FEMALE 2class Worker(BaseModel):name: strgender: GenderEnum问什么叫前后端解耦与展示扩展性答前端传 1数据库存 1那前端页面怎么显示“男/女”呢如果前端自己写死 1男, 2女以后你要加一个 3未知前端、后端、数据库全都要改。标准的做法是后端提供一个接口把枚举列表包含 code 和 label返回给前端。前端拿到后去渲染下拉框和列表。这样以后无论怎么加选项前端代码一行都不用动。问通过数据校验和枚举类 感觉后端不相信任何前端做的数据过滤努力答用一句话来概括就是“前端校验是为了哄用户开心后端校验是为了保后端平安。”1. 前端校验属于“用户体验UX”范畴它的目的是为了让用户在点击“提交”按钮之前就能知道“哎呀我密码少填了一位”从而立刻修改。它的价值极大地提升了用户体验减少了无意义的网络请求减轻了服务器的压力。它的本质前端是“门面担当”它负责貌美如花把界面做得漂漂亮亮、交互做得顺顺滑滑。2. 后端校验属于“系统安全与业务底线”范畴它的目的是为了保证数据库的绝对纯洁以及业务逻辑的绝对安全。它的价值哪怕前端代码被黑客删光了哪怕有人用脚本恶意攻击系统依然能坚挺地拒绝非法数据。它的本质后端是“保安大爷”它负责稳如泰山死死守住大门不管外面是谁没有通行证合法数据绝对不让进。