feat:新增大屏存取发布接口

This commit is contained in:
2026-10-11 08:27:30 -07:00
parent dadd797391
commit afc07ba695
23 changed files with 2672 additions and 36 deletions
+71
View File
@@ -0,0 +1,71 @@
"""限制请求体大小:声明的 Content-Length 超限直接 413,没声明(分块)或声明得不实的,边读边数
FastAPI 会先把整个请求体收完再调用接口函数,超大的请求体已经占满了带宽和内存
和数据文件上传的 UploadSizeLimit 同一出发点;限制随路径而不同,所以用规则表,第一条命中的生效
"""
import re
from dataclasses import dataclass
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException
from starlette.types import ASGIApp, Message, Receive, Scope, Send
from app.common.response import error_content
class BodyTooLarge(HTTPException):
"""边读边数时超限;message 由全局 HTTP 异常处理放进信封,和声明超限的响应文案一致"""
def __init__(self, message: str):
super().__init__(413)
self.message = message
@dataclass(frozen=True)
class BodyLimitRule:
methods: frozenset[str]
path: re.Pattern[str]
max_bytes: int
message: str
class BodyLimit:
def __init__(self, app: ASGIApp, rules: list[BodyLimitRule]):
self.app = app
self.rules = rules
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
rule = self._match(scope) if scope["type"] == "http" else None
if rule is None:
await self.app(scope, receive, send)
return
length = dict(scope["headers"]).get(b"content-length", b"")
if length.isdigit() and int(length) > rule.max_bytes:
response = JSONResponse(error_content(413, rule.message), 413)
await response(scope, receive, send)
return
received = 0
async def counted() -> Message:
nonlocal received
message = await receive()
if message["type"] == "http.request":
received += len(message.get("body", b""))
if received > rule.max_bytes:
# 抛出的异常由应用里统一的 HTTP 异常处理转成 413 信封
raise BodyTooLarge(rule.message)
return message
await self.app(scope, counted, send)
def _match(self, scope: Scope) -> BodyLimitRule | None:
return next(
(
rule
for rule in self.rules
if scope["method"] in rule.methods
and rule.path.fullmatch(scope["path"])
),
None,
)
+2
View File
@@ -50,6 +50,8 @@ async def handle_http_exception(_request: Request, exc: HTTPException) -> JSONRe
message = HTTPStatus(exc.status_code).phrase
except ValueError:
message = "请求失败"
# 个别异常自带面向用户的文案,例如请求体超限
message = getattr(exc, "message", None) or message
return JSONResponse(
status_code=exc.status_code,
content=error_content(exc.status_code, message),