如何使用FastAPI和Swagger允许任意查询参数?

myzjeezk  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(298)

注意:此问题与here问题不同,因为我需要它来处理Swagger。

给定一个FastAPI GET端点,我希望允许任意的URL参数集,同时保持对Swagger的支持。
我的用例是希望支持一组类似JSON API的查询参数,如下所示:

/api/books/?include=author&sort=name,zip&sort[author]=-lname&fields=name,phone,street

方括号的使用使我无法使用传统的类来建模查询参数,所以我直接使用Request对象来代替。然而,我想使用Swagger来测试端点。我找不到一种方法来提供任意的URL参数。我很乐意将它们作为单个字符串键入。
人们可能会这样想:

def books(**params): 
    ....

这给出了一个curl语句:

api/books?params=sort%5Bone%5D%3Dtwo'

我真正想要是:

api/books?sort&one%5D%3Dtwo'
puruo6ea

puruo6ea1#

您可以使用可选字符串参数(在下面的示例中为book_params)通过OpenAPI(Swagger UI)将查询参数作为单个字符串传递,例如include=author&sort=name,zip&sort[author]=-lname&fields=name,phone,street。然后,您可以解析查询数据(使用urllib.parse.parse_qs)以获得字典,如下所示。
下面的例子也使用了here描述的方法,以便修复parse_qs将单个值解析为列表的部分(例如,'foo=bar'将被解析为foo = ['bar']),同时还保留用户传递给列表的键的所有值。例如,如果用户在URL中多次传递相同的键,即,例如,'foo=2&bar=7&foo=10',则使用dict(request.query_params)检索查询参数将得到{"foo":"10","bar":"7"}而不是{"foo":["2","10"],"bar":"7"}。但是,下面的示例中演示的方法(使用上述方法)也处理了这一问题,方法是解析query string(可以使用request.url.query检索),并确保保留实际列表。
可以通过检查这个可选参数book_params是否为空,来决定是否使用book_params读取查询参数(意味着请求通过Swagger发送)或直接使用Request对象(意味着通过在浏览器的地址栏中键入URL来发送请求,例如http://127.0.0.1:8000/api/books?include=author&sort=name,zip&sort[author]=-lname&fields=name,phone,street,或者使用其他客户端应用程序)。请确保为该可选参数(例如book_params)命名为唯一的名称,该名称不会也是实际参数的一部分。

from fastapi import FastAPI, Request
from typing import Optional
from urllib.parse import parse_qs

app = FastAPI()

@app.get("/api/books")
def books(request: Request, book_params: Optional[str] = None):
    q_params = {}

    if book_params is not None:
        q_params = parse_qs(book_params, keep_blank_values=True)
    else:   
        q_params = parse_qs(request.url.query, keep_blank_values=True)

    d = dict((k, v if len(v)>1 else v[0]) 
                for k, v in q_params.items())

    return d

相关问题