FastAPI is not routing URL containing slash encoded.
from fastapi import FastAPI
app = FastAPI()
@app.get("/units/{unit}")
def read_unit(unit: str):
return {"unit": unit}
/units/kg%2Fs.{"detail":"Not Found"}.{"unit": "kg/s"}./units/kg, returning {"unit": "kg"}.FastAPI working with URL encoded slash
Starlette regexp seems fine though when testing it returns this route regexp:
'^/units/(?P<unit>[^/]+)$'
>>> import re
>>> pattern=re.compile('^/units/(?P<unit>[^/]+)$')
>>> pattern.match('/units/kg%2Fs')
<re.Match object; span=(0, 13), match='/units/kg%2Fs'>
>>> pattern.match('/units/kg%2Fs').groupdict()["unit"]
'kg%2Fs'
Seems like this is probably an issue with starlette -- I'd maybe try to create a reproducible example using only starlette (should be very similar to what you've done already) and create an issue there.
For what it's worth, it would also be easier to test using the starlette TestClient -- that way you wouldn't need to access the endpoint in a browser.
Yeah, tried it with the latest version of starlette & I've encountered the same issue. I'll put an issue in their tracker.
Thanks @jules-ch and @dmontagu !
Then let's track that in Starlette, right?
@tiangolo Sorry it was on my todo at some point & I completely forgot.
The issue has been transfered to the starlette repo tracker.
As stated there https://github.com/encode/starlette/issues/826#issuecomment-593124738.
It seems to be starlette related linked with the current ASGI spec.
The path is decoded & the routing is made on this decoded path.
The ASGI spec has implemented a raw_path parameter but for now its better to stick to query parameters for those kind of use cases.
I'm closing it for now.
Thanks for reporting back and closing the issue @jules-ch ! :+1:
I'm running in the same issue. Using a query parameter is not really feasible in all cases or a proper way to build an API though. I hope some changes from the ASGI spec updates can be streamlined into Starlette/FastAPI.
@jules-ch
so, how should I deal with this?
@Gatsby-Lee
If you want to keep this kind of behavior with slash in your routing path.
You can use this workaround with a specific character to split your url, like - or #.
We can do that with starlette with the routing convertors path (https://www.starlette.io/routing/).
Keeping my example, you can try the following:
from fastapi import FastAPI
app = FastAPI()
@app.get("/units/-/{unit:path}")
def read_unit(unit: str):
return {"unit": unit}
You can now visit the URL http://127.0.0.1:8000/units/-/kg/s which will outputs :
{"unit":"kg/s"}
I know Gitlab is using this to route their subgroups & repositories.
@jules-ch I see. Thank you for your comment. I will try what you shared :)