From abfd41f31e76a11619f6aa233058aa0bb25c2dec Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Thu, 2 Dec 2021 23:22:31 -0800 Subject: [PATCH] change(fastapi): centralize HTTPException Signed-off-by: Kevin Morris --- aurweb/asgi.py | 24 +++++++++++++----------- aurweb/routers/errors.py | 21 --------------------- templates/errors/404.html | 8 -------- templates/errors/503.html | 8 -------- templates/errors/detail.html | 8 ++++++++ test/test_asgi.py | 10 +++++++--- 6 files changed, 28 insertions(+), 51 deletions(-) delete mode 100644 aurweb/routers/errors.py delete mode 100644 templates/errors/404.html delete mode 100644 templates/errors/503.html create mode 100644 templates/errors/detail.html diff --git a/aurweb/asgi.py b/aurweb/asgi.py index b399cfb1..ef8d5933 100644 --- a/aurweb/asgi.py +++ b/aurweb/asgi.py @@ -6,8 +6,8 @@ import typing from urllib.parse import quote_plus -from fastapi import FastAPI, HTTPException, Request -from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi import FastAPI, HTTPException, Request, Response +from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles from prometheus_client import multiprocess from sqlalchemy import and_, or_ @@ -21,10 +21,11 @@ from aurweb.auth import BasicAuthBackend from aurweb.db import get_engine, query from aurweb.models import AcceptedTerm, Term from aurweb.prometheus import http_api_requests_total, http_requests_total, instrumentator -from aurweb.routers import accounts, auth, errors, html, packages, rpc, rss, sso, trusted_user +from aurweb.routers import accounts, auth, html, packages, rpc, rss, sso, trusted_user +from aurweb.templates import make_context, render_template # Setup the FastAPI app. -app = FastAPI(exception_handlers=errors.exceptions) +app = FastAPI() # Instrument routes with the prometheus-fastapi-instrumentator # library with custom collectors and expose /metrics. @@ -93,14 +94,15 @@ def child_exit(server, worker): # pragma: no cover @app.exception_handler(HTTPException) -async def http_exception_handler(request, exc): - """ - Dirty HTML error page to replace the default JSON error responses. - In the future this should use a proper Arch-themed HTML template. - """ +async def http_exception_handler(request: Request, exc: HTTPException) \ + -> Response: + """ Handle an HTTPException thrown in a route. """ phrase = http.HTTPStatus(exc.status_code).phrase - return HTMLResponse(f"

{exc.status_code} {phrase}

{exc.detail}

", - status_code=exc.status_code) + context = make_context(request, phrase) + context["exc"] = exc + context["phrase"] = phrase + return render_template(request, "errors/detail.html", context, + exc.status_code) @app.middleware("http") diff --git a/aurweb/routers/errors.py b/aurweb/routers/errors.py deleted file mode 100644 index 9ed1e80d..00000000 --- a/aurweb/routers/errors.py +++ /dev/null @@ -1,21 +0,0 @@ -from http import HTTPStatus - -from aurweb.templates import make_context, render_template - - -async def not_found(request, exc): - context = make_context(request, "Page Not Found") - return render_template(request, "errors/404.html", context, - HTTPStatus.NOT_FOUND) - - -async def service_unavailable(request, exc): - context = make_context(request, "Service Unavailable") - return render_template(request, "errors/503.html", context, - HTTPStatus.SERVICE_UNAVAILABLE) - -# Maps HTTP errors to functions -exceptions = { - 404: not_found, - 503: service_unavailable -} diff --git a/templates/errors/404.html b/templates/errors/404.html deleted file mode 100644 index 4926aff6..00000000 --- a/templates/errors/404.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends 'partials/layout.html' %} - -{% block pageContent %} -
-

404 - {% trans %}Page Not Found{% endtrans %}

-

{% trans %}Sorry, the page you've requested does not exist.{% endtrans %}

-
-{% endblock %} diff --git a/templates/errors/503.html b/templates/errors/503.html deleted file mode 100644 index 9a0ed56a..00000000 --- a/templates/errors/503.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends 'partials/layout.html' %} - -{% block pageContent %} -
-

503 - {% trans %}Service Unavailable{% endtrans %}

-

{% trans %}Don't panic! This site is down due to maintenance. We will be back soon.{% endtrans %}

-
-{% endblock %} diff --git a/templates/errors/detail.html b/templates/errors/detail.html new file mode 100644 index 00000000..f382a9bb --- /dev/null +++ b/templates/errors/detail.html @@ -0,0 +1,8 @@ +{% extends 'partials/layout.html' %} + +{% block pageContent %} +
+

{{ "%d" | format(exc.status_code) }} - {{ phrase }}

+

{{ exc.detail }}

+
+{% endblock %} diff --git a/test/test_asgi.py b/test/test_asgi.py index fa2df5a1..16b07c31 100644 --- a/test/test_asgi.py +++ b/test/test_asgi.py @@ -11,6 +11,8 @@ import aurweb.asgi import aurweb.config import aurweb.redis +from aurweb.testing.requests import Request + @pytest.mark.asyncio async def test_asgi_startup_session_secret_exception(monkeypatch): @@ -42,9 +44,11 @@ async def test_asgi_startup_exception(monkeypatch): async def test_asgi_http_exception_handler(): exc = HTTPException(status_code=422, detail="EXCEPTION!") phrase = http.HTTPStatus(exc.status_code).phrase - response = await aurweb.asgi.http_exception_handler(None, exc) - assert response.body.decode() == \ - f"

{exc.status_code} {phrase}

{exc.detail}

" + response = await aurweb.asgi.http_exception_handler(Request(), exc) + assert response.status_code == 422 + content = response.body.decode() + assert f"{exc.status_code} - {phrase}" in content + assert "EXCEPTION!" in content @pytest.mark.asyncio