How to pass URL as a path parameter to a FastAPI route?
Question:
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
Answers:
Option 1
You can simply use the 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 below:
http://127.0.0.1:8000/https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg
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
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 {"detail":"Method Not Allowed"}
error.
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/
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 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 below:
http://127.0.0.1:8000/https://raw.githubusercontent.com/ultralytics/yolov5/master/data/images/zidane.jpg
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
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 {"detail":"Method Not Allowed"}
error.
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/