简体   繁体   中英

How to pass URL as a path parameter to a FastAPI route?

I have created a simple API using FastAPI, and I am trying to pass a URL to a FastAPI route as an arbitrary path parameter.

from fastapi import FastAPI
app = FastAPI()
@app.post("/{path}")
def pred_image(path:str):
    print("path",path)
    return {'path':path}

When I test it, it doesn't work and throws an error. I am testing it this way:

http://127.0.0.1:8000/https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg

Option 1

You can simply use the path convertor, as described in Starlette documentation , to get the path , including any additional / characters.

@app.api_route('/{_:path}', methods=["GET", "POST"]) 
def pred_image(request: Request):
    return {"path": request.url.path[1:]}

Test using the below:

http://127.0.0.1:8000/https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg

Option 2

As @luk2302 mentioned in the comments section, your client (ie, either end user, javascript, etc) needs to encode the URL. The encoded URL, however, as provided by @luk2302 does not seem to work, leading to a "detail": "Not Found" error. As it turns out, you would need to encode it twice to work. That is:

http://127.0.0.1:8000/https%253A%252F%252Fraw.githubusercontent.com%252Fultralytics%252Fyolov5%252Fmaster%252Fdata%252Fimages%252Fzidane.jpg

On server side, you can decode the URL (twice) as follows:

from urllib.parse import unquote 

@app.post("/{path}")
def pred_image(path: str):
    return {'path':unquote(unquote(path))}  

Option 3

Since your endpoint seems to accept POST requests, you might consider having the client sending the image URL in the body of the request, instead of sending it as a path parameter. Please have a look at the answers here and here , as well as FastAPI's documentation , on how to do that.


Note:

If you are testing this through typing the aforementioned URLs into the address bar of a browser - instead of some kind of other client, eg, through Javascript, Python requests, etc. - you need to change the method of your endpoint to GET only (ie, for Option 1: @app.api_route('/{_:path}', methods=["GET"]) and for Option 2: @app.get("/{path}") ). Otherwise, you would come accross {"detail":"Method Not Allowed"} error, if attempted testing this through the browser's address bar (as when you type a URL in the address bar of your browser, it performs a GET request).

example: http://127.0.0.1:8000/porxy/https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg

this passed url doesn't have any ? query params, just need

@app.get('/proxy/{url:path}')
async def proxy(url:str):
    # url:https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg 
    return url  

{var:path} could match : and / , but will stop at ? , so if you want to get the full url, you need from fastapi import Request . the req.val contain the full url, include the host. example: http://127.0.0.1:8000/porxy/http://www.xx.com/abc?query=abc

PS: HttpUrl used to check the passed url is or not legal

from pydantic import HttpUrl  
from fastapi import FastAPI, Request  
  
app = FastAPI()  
  
  
# example request url http://127.0.0.1:8000/proxy/http://www.xx.com/abc?query=abc  
@app.get('/proxy/{url:path}', )  
async def proxy(url: HttpUrl, req: Request):  
    # url: http://www.xx.com/abc  
    # req.url: http://127.0.0.1:8000/porxy/http://www.xx.com/abc?query=abc    
    # wanted_url: http://www.xx.com/abc?query=abc  
    wanted_url = str(req.url).partition('/proxy/')[-1]  
    return {'url': url, 'req_url': str(req.url), 'wanted_url': wanted_url}  
  
  
if __name__ == '__main__':  
    import uvicorn  
    uvicorn.run('main:app', port=8000)

Path convertor https://fastapi.tiangolo.com/tutorial/path-params/#path-convertor

Using an option directly from Starlette you can declare a path parameter containing a path using a URL like:

/files/{file_path:path}

In this case, the name of the parameter is file_path , and the last part, :path , tells it that the parameter should match any path .

So, you can use it with:

from fastapi import FastAPI  app = FastAPI()  
@app.get("/files/{file_path:path}") 
async def read_file(file_path: str):     
    return {"file_path": file_path}

starlette Request : https://www.starlette.io/requests/

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