FastAPI后台开发基础(4):FastAPI 官网文档错误、编码错误以及注意要点记录

设置查询参数是否为必填

使用Annotated装饰查询参数

不设置default值

代码语言:python代码运行次数:0运行复制
@app.get("/validation")
async def async_root(str_param: Annotated[str | None, Query(min_length = 3,
                                                            max_length = 20,
                                                            pattern = '^[a-zA-Z0-9]+$',
                                                            description = 'This is a string parameter')]):
    """
    如果不设置 str_param = None, 则 str_param 为必填
    """
    ret = {"str_list": str_param}
    if str_param is not None:
        h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
        ret.update({"sha3_512": h})
    else:
        ret.update({"uuid": uuid.uuid4().hex})
    return ret
不设置默认值
生成的文档中参数为必填

设置default值

代码语言:python代码运行次数:0运行复制
@app.get("/validation1")
async def async_root1(str_param: Annotated[str | None, Query(min_length = 3,
                                                             max_length = 20,
                                                             pattern = '^[a-zA-Z0-9]+$',
                                                             description = 'This is a string parameter')] = None):
    """
    设置 str_param = None 表明允许 str_param 为空(非必填)
    """
    ret = {"str_list": str_param}
    if str_param is not None:
        h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
        ret.update({"sha3_512": h})
    else:
        ret.update({"uuid": uuid.uuid4().hex})
    return ret
设置 default 值
生成的文档中参数为非必填

string 类型的默认值

代码语言:python代码运行次数:0运行复制
@app.get("/validation2")
async def async_root2(str_param: Annotated[str, Query(min_length = 3,
                                                      max_length = 20,
                                                      pattern = '^[a-zA-Z0-9]+$',
                                                      description = 'This is a string parameter')] = 'default_value'):
    """
    此时 str_list 有默认值,非必填
    """
    ret = {"str_list": str_param}
    if str_param is not None:
        h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
        ret.update({"sha3_512": h})
    else:
        ret.update({"uuid": uuid.uuid4().hex})
    return ret
默认值为 string 类型

使用ellipsis作为默认值

错误写法
代码语言:python代码运行次数:0运行复制
@app.get("/validation3")
async def async_root3(str_param: Annotated[str, Query(min_length = 3,
                                                      max_length = 20,
                                                      pattern = '^[a-zA-Z0-9]+$',
                                                      description = 'This is a string parameter')] = ...):
    """
    此时 str_list = ... 生成的文档中,str_list 并非为必填
    并且当发送请求:
        curl -X 'GET' 'http://127.0.0.1:18081/validation3' -H 'accept: application/json'
    后台会报错:
        ValueError: [TypeError("'ellipsis' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
    """
    ret = {}
    if str_param is not ...:
        ret = {"str_list": str_param}
        h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
        ret.update({"sha3_512": h})
    else:
        ret.update({"uuid": uuid.uuid4().hex, 'error': 'str_list is ...'})
    return ret


@app.get("/validation4")
async def async_root4(str_param: Annotated[str | None, Query(min_length = 3,
                                                             max_length = 20,
                                                             pattern = '^[a-zA-Z0-9]+$',
                                                             description = 'This is a string parameter')] = ...):
    """
    发送:curl -X 'GET' 'http://127.0.0.1:18081/validation4' -H 'accept: application/json'时
    后台会报错:ValueError: [TypeError("'ellipsis' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
    """
    ret = {}
    if str_param is not ...:
        ret = {"str_list": str_param}
        h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
        ret.update({"sha3_512": h})
    else:
        ret.update({"uuid": uuid.uuid4().hex, 'error': 'str_list is ...'})
    return ret

使用 Query(xxxx) = ...的写法均会引起错误,此种写法来源于 FastAPI 官网的文档:

使用ellipsis设置必填参数

错误的文档示例

关于此错误写法笔者已经给 FastAPI 提了 Issue:

相关开发者回复
正确写法
代码语言:python代码运行次数:0运行复制
@app.get("/validation5")
async def async_root5(str_param: Annotated[str, Query(default = ...,
                                                      min_length = 3,
                                                      max_length = 20,
                                                      pattern = '^[a-zA-Z0-9]+$',
                                                      description = 'This is a string parameter')]):
    """
    使用 Query(default = ...) 的方式设置 str_list 为必填参数
    不使用 default 时参数也会默认为必填
    Remember that in most of the cases, when something is required,
    you can simply omit the default, so you normally don't have to use ...
    """
    ret = {"str_list": str_param}
    h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
    ret.update({"sha3_512": h})
    ret.update({"uuid": uuid.uuid4().hex})
    return ret
使用ellipsis作为默认值来设定必填属性

官网文档中关于必填参数可以为None的错误描述

官网文档描述链接

如果一个参数为必填同时还可以为 None 的错误描述

关于这个问题也提了 Issue 给到 FastAPI 团队:Question about "Required, can be None"

问题得到了 FastAPI 开发者的确认

关于这个问题的讨论帖:Discussion,感兴趣的可以自行查看。

Annotated搭配 Query(default=xxxx)带来的问题

代码语言:python代码运行次数:0运行复制
@app.get("/validation1_1")
async def async_root1_1(str_param: Annotated[str | None, Query(default = 'test',
                                                               min_length = 3,
                                                               max_length = 20,
                                                               pattern = '^[a-zA-Z0-9]+$',
                                                               description = 'This is a string parameter')]):
    ret = {"str_list": str_param}
    if str_param is not None:
        h = hashlib.sha3_512(str_param.encode('utf-8')).hexdigest()
        ret.update({"sha3_512": h})
    else:
        ret.update({"uuid": uuid.uuid4().hex})
    return ret
使用 Query 的 default 参数设置默认值

运行时会报错:

代码语言:shell复制
AssertionError: `Query` default value cannot be set in `Annotated` for 'str_param'. Set the default value with `=` instead.
Amazon Q AI 给出的答复

总之在使用Annotated搭配 Query 时需要注意编码细节。

列表查询参数

代码语言:python代码运行次数:0运行复制
@app.get("/validation7")
async def async_root7(str_list: Annotated[List[str] | None, Query(min_length = 1)]):
    """
    列表查询参数
    curl -X 'GET' 'http://127.0.0.1:18081/validation7?str_list=aaa&str_list=bbb&str_list=ccc' -H 'accept: application/json'
    """
    if str_list is not None:
        return {'items_count': len(str_list), 'items': str_list}
    else:
        return {'message': 'no items'}
运行效果

路径参数使用alias别名的注意点

代码语言:python代码运行次数:0运行复制
@app.get('/{item_id}')
async def async_root(item_id: Annotated[int, Path(title = 'Item ID', alias = 'xid')]):
    """
    GithubIssue: 
    当带有 alias 时,接口无法正常工作:
        curl http://127.0.0.1:18081/123
        {"detail":[{"type":"missing","loc":["path","xid"],"msg":"Field required","input":null}]}
    """
    return {'item_id': item_id}

@app.get('/{xid_2}')
async def async_root2(item_id: Annotated[int, Path(title = 'Item ID', alias = 'xid_2')]):
    """
    The title parameter you're referring to in the Path function is part of FastAPI's path parameter customization. While it doesn't directly appear in the Swagger UI documentation, it serves an important purpose in the OpenAPI schema.
    Here's what the title parameter does:
        OpenAPI Schema: The title is included in the OpenAPI (formerly Swagger) schema that FastAPI generates.
                        This schema is a machine-readable description of your API, which can be used by various tools and clients.
        JSON Schema: In the context of JSON Schema (which OpenAPI uses), the title provides a human-readable name for the parameter.
                     It's meant to be a brief description of the parameter.
        API Documentation: While not directly visible in the Swagger UI,
                           the title can be used by other documentation tools that consume the OpenAPI schema to provide more descriptive names for parameters.
        Code Generation: Tools that generate client code from OpenAPI schemas might use the title to create more meaningful variable or property names.
    """
    return {'item_id': item_id}

相关 issue 讨论帖:Should the alias parameter in the Path method be removed?

相关回复