将JWT字符串反序列化为嵌套Pydantic模型时遇验证错误求助
问题:JWT字符串反序列化为嵌套Pydantic模型失败
尝试将JWT字符串直接反序列化为嵌套Pydantic模型,使用@model_validator(mode="before")在验证前解码JWT并合并payload,但调用model_validate时抛出ValidationError,提示无法将字符串传入嵌套模型。
代码示例
from typing import Dict, Any import jwt from pydantic import BaseModel, model_validator class JwtDecodeError(Exception): """自定义JWT解码异常""" pass class JwtBaseModel(BaseModel): session_jwt: str @classmethod @model_validator(mode="before") def decode_jwt(cls, values: Dict[str, Any]) -> Dict[str, Any]: """无需签名验证解码JWT""" token = values.get("session_jwt") if not token: raise ValueError("必须提供JWT token") try: decoded_payload = jwt.decode(token, options={"verify_signature": False}) except jwt.DecodeError as e: raise JwtDecodeError(f"JWT解码失败: {str(e)}") values.update(decoded_payload) return values class UsernameSignature(JwtBaseModel): valid: bool busy: bool exp: int payment_required: bool class Username(BaseModel): busy: bool meta: dict payment_required: bool signature: UsernameSignature valid: bool username: str model_json = { "busy": False, "meta": { "gameSessionId": "", "tracing": None }, "payment_required": True, "signature": "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6ImpkamRvd2RiIiwidmFsaWQiOnRydWUsImJ1c3kiOmZhbHNlLCJleHAiOjE3MjY3NzY1OTksInBheW1lbnRfcmVxdWlyZWQiOnRydWV9.VjCpW2rY8MEgcVKJrx9Igz05-OnPdJuZxmg4wFUuy25Ixik02O9BkqXoOrfdgo0QR_-BPzB1AdAoMoveu3eqVHwtKGOXzG__eU6kObOlcowqB7iu9SwfK6TXmh1djaxhCCOveOIcj3UOlXQIZ4b6JuFSkezzHAUfaBe2KZskhyFH107MPxLnwWnVL3jOJwOcM7ierd96Y1xyDqmBlL9k8hotTWHwsNlTi0H2GjXWdMk8c54eMGIBhma66q_S__LQ9k1VSZkIj1awOpPzBrUdxMNw9B6J1bsPN8ajLIrLM_oHrXWGDGpIK9Clmbi6XfPe87X09pKutRapKU5nCqOWog", "username": "jdjdowdb", "valid": True } parsed_model = Username.model_validate(model_json)
错误信息
pydantic_core._pydantic_core.ValidationError: 1 validation error for Username signature Input should be a valid dictionary or instance of UsernameSignature [type=model_type, input_value='eyJhbGciOiJSUzI1NiJ9.eyJ...e87X09pKutRapKU5nCqOWog', input_type=str]
解决方案
问题根源
当前JwtBaseModel的decode_jwt验证器仅处理包含session_jwt键的字典输入,但嵌套字段signature传入的是纯JWT字符串,而非字典,导致验证器未触发,Pydantic直接尝试将字符串转换为UsernameSignature实例,引发错误。
方法一:修改JwtBaseModel支持直接处理字符串输入
调整验证器,使其能识别字符串输入并直接解码:
class JwtBaseModel(BaseModel): @classmethod @model_validator(mode="before") def decode_jwt(cls, values: Any) -> Dict[str, Any]: """支持直接解码字符串JWT或含session_jwt的字典""" if isinstance(values, str): token = values else: token = values.get("session_jwt") if not token: raise ValueError("必须提供JWT token") try: decoded_payload = jwt.decode(token, options={"verify_signature": False}) except jwt.DecodeError as e: raise JwtDecodeError(f"JWT解码失败: {str(e)}") return decoded_payload # 移除JwtBaseModel中的session_jwt字段,现在直接处理字符串输入 class UsernameSignature(JwtBaseModel): valid: bool busy: bool exp: int payment_required: bool
方法二:在父模型中用字段验证器处理signature字段
无需修改JwtBaseModel,直接在Username模型中添加字段验证器解码JWT:
from pydantic import field_validator class Username(BaseModel): busy: bool meta: dict payment_required: bool signature: UsernameSignature valid: bool username: str @field_validator("signature", mode="before") def decode_signature(cls, v: str) -> UsernameSignature: try: decoded_payload = jwt.decode(v, options={"verify_signature": False}) return UsernameSignature(**decoded_payload) except jwt.DecodeError as e: raise JwtDecodeError(f"签名JWT解码失败: {str(e)}")
两种方法都能实现将JWT字符串解码后填充到嵌套模型字段中,可根据实际场景选择:方法一更通用,适合所有继承JwtBaseModel的类直接处理JWT字符串;方法二更灵活,针对特定字段定制解码逻辑。
内容的提问来源于stack exchange,提问作者user27385035
相关产品推荐
相关产品推荐

