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

Option 1

You could simply use Starlette’s path convertor to capture arbitrary paths. As per Starlette documentation, path returns the rest of the path, including any additional / characters.

from fastapi import Request

@app.get('/{_:path}')
def pred_image(request: Request):
    return {'path': request.url.path[1:]}

or

@app.get('/{full_path:path}')
def pred_image(full_path: str):
    return {'path': full_path}

Test using the link below:

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

Please note that the URL above will be automatically encoded by the browser (as URLs can only contain ASCII characters), meaning that before sending the request, any special characters will be converted to other reserved ones, using the % sign followed by a hexadecimal pair. Hence, behind the scenes the request URL should look like this:

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

If, for instance, you would like to test the endpoint through other clients, such as using Python requests lib, you should then encode the URL yourself before sending the request. You can do that using Python’s urllib.parese.quote() function, as shown below. Note that, the quote() function considers / characters safe by default, meaning that any / characters won’t be encoded. Hence, in this case, you should set the safe parameter to '' (i.e., empty string), in order to encode / characters as well.

Test using Python requests:

import requests
from urllib.parse import quote 

base_url="http://127.0.0.1:8000/"
path_param = 'https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg'
url = base_url + quote(path_param, safe="")

r = requests.get(url)
print(r.json())

Output:

{"path":"https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg"}

Test using HTML <form>:

If you would like to test the above by passing the URL through an HTML <form>, instead of manually typing it after the base URL, please have a look at Option 3 of this answer, which demonstrates how to convert the form <input> element into a path parameter on <form> submission.

Option 2

As @luk2302 mentioned in the comments section, your client (i.e., 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.get('/{path}')
def pred_image(path: str):
    return {'path':unquote(unquote(path))}  

Option 3

Use a query parameter instead, as shown below:

@app.get("https://stackoverflow.com/")
def pred_image(path: str):
    return {'path': path}

Test using the link below:

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

If, again, you would like to use Python requests lib to test the endpoint above, have a look at the example below. Note that since the image URL is now being sent as part of the query string (i.e., as a query parameter), requests will take care of the URL encoding; hence, there is no need for using the quote() function this time.

Test using Python requests:

import requests

base_url="http://127.0.0.1:8000/"
params = {'path': 'https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg'}
r = requests.get(base_url, params=params)
print(r.json())

Option 4

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 passing it as a path parameter. Please have a look at the answers here, 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, then keep using @app.get() routes, as when you type a URL in the address bar of your browser, it performs a GET request. If , however, you need this to work with POST requests, you will have to change the endpoint’s decorator to @app.post() (as shown in your question); otherwise, you would come accross 405 "Method Not Allowed" error (see here for more details on such errors).

Leave a Comment