add aurweb.asgi.id_redirect_middleware

A new middleware which redirects requests going to '/route?id=some_id'
to '/route/some_id'. In the FastAPI application, we'll prefer using
restful layouts where possible where resource-based ids are
parameters of the request uri: '/route/{resource_id}'.

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-06-19 01:10:53 -07:00
parent e534704a98
commit dc4cc9b604
2 changed files with 32 additions and 0 deletions

View file

@ -2,6 +2,8 @@ import asyncio
import http
import typing
from urllib.parse import quote_plus
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
@ -120,3 +122,23 @@ async def check_terms_of_service(request: Request, call_next: typing.Callable):
task = asyncio.create_task(call_next(request))
await asyncio.wait({task}, return_when=asyncio.FIRST_COMPLETED)
return task.result()
@app.middleware("http")
async def id_redirect_middleware(request: Request, call_next: typing.Callable):
id = request.query_params.get("id")
if id is not None:
# Preserve query string.
qs = []
for k, v in request.query_params.items():
if k != "id":
qs.append(f"{k}={quote_plus(str(v))}")
qs = str() if not qs else '?' + '&'.join(qs)
path = request.url.path.rstrip('/')
return RedirectResponse(f"{path}/{id}{qs}")
task = asyncio.create_task(call_next(request))
await asyncio.wait({task}, return_when=asyncio.FIRST_COMPLETED)
return task.result()

View file

@ -148,3 +148,13 @@ def test_nonce_csp():
if not (nonce_verified := (script.get("nonce") == nonce)):
break
assert nonce_verified is True
def test_id_redirect():
with client as request:
response = request.get("/", params={
"id": "test", # This param will be rewritten into Location.
"key": "value", # Test that this param persists.
"key2": "value2" # And this one.
}, allow_redirects=False)
assert response.headers.get("location") == "/test?key=value&key2=value2"