简体   繁体   中英

Add route to FastAPI with custom path parameters

I am trying to add some routes to a FastAPI project. New routes are not determined beforehand and the code should read them from a file. To add routes I am using add_api_route as below:

from fastapi import APIRouter

my_router = APIRouter()

def foo(xyz):
    return {"Result": xyz}

my_router.add_api_route('/foo/{xyz}', endpoint=foo)

Above works fine.

However enrty path parameters are not fixed and I need to read them from a file, to achieve this, I am trying something like this:

from fastapi import APIRouter

my_router = APIRouter()

def foo(**kwargs):
    return {"Result": kwargs['xyz']}

read_from_file = '/foo/{xyz}' # Assume this is read from a file

my_router.add_api_route(read_from_file, endpoint=foo)

But it throws this error:

{"detail":[{"loc":["query","kwargs"],"msg":"field required","type":"value_error.missing"}]}

FastAPI tries to find actual argument xyz in foo signature.

Is there any way in FastAPI to achieve this? Or even any solution to accept a path like /foo/... whatever.../ ?

This will generate a function with a new signature (I assume every parameter is a string):

from fastapi import APIRouter
import re
import inspect

my_router = APIRouter()


def generate_function_signature(route_path: str):
    args = {arg: str for arg in re.findall(r'\{(.*?)\}', route_path)}
    
    def new_fn(**kwargs):
        return {"Result": kwargs['xyz']}

    params = [
        inspect.Parameter(param,
                         inspect.Parameter.POSITIONAL_OR_KEYWORD,
                         annotation=type_)
        for param, type_ in args.items()
    ]

    new_fn.__signature__ = inspect.Signature(params)
    new_fn.__annotations__ = args
    return new_fn


read_from_file = '/foo/{xyz}' # Assume this is read from a file

my_router.add_api_route(read_from_file, endpoint=generate_function_signature(read_from_file))


read_from_file = '/foo/bar/{xyz}' # Assume this is read from a file

my_router.add_api_route(read_from_file, endpoint=generate_function_signature(read_from_file))

However I am sure there is a better way of doing whatever you are trying to do, but I would need to understand your problem first

As described here , you can use the path convertor, provided by Starlette , to capture arbitrary paths. If you are about to pass **kwargs to your route as part of the path, eg, http://127.0.0.1:8000/foo/foo=2&bar=7 , then you can parse the path string and get a dictionary using urllib.parse.parse_qs , as demonstrated in this answer (similar to what you would do, if you had to parse a query string). The below also utilises the method described here . Example below:

from fastapi import APIRouter, FastAPI
from urllib.parse import parse_qs

app = FastAPI()
my_router = APIRouter()

def foo(full_path: str):
    params  = parse_qs(full_path)
    d = dict( (k, v if len(v)>1 else v[0]) 
                for k, v in params.items() )
    return {"path": full_path, "params": d}
    
route_path = '/foo/{full_path:path}'
my_router.add_api_route(route_path, endpoint=foo)
app.include_router(my_router)

Input test:

http://127.0.0.1:8000/foo/foo=2&bar=7&foo=10

Output:

{"path":"foo=2&bar=7&foo=10","params":{"foo":["2","10"],"bar":"7"}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM