change(fastapi): centralize HTTPException

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-12-02 23:22:31 -08:00
parent a747548254
commit abfd41f31e
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
6 changed files with 28 additions and 51 deletions

View file

@ -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"<h1>{exc.status_code} {phrase}</h1><p>{exc.detail}</p>",
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")

View file

@ -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
}

View file

@ -1,8 +0,0 @@
{% extends 'partials/layout.html' %}
{% block pageContent %}
<div id="error-page" class="box 404">
<h2>404 - {% trans %}Page Not Found{% endtrans %}</h2>
<p>{% trans %}Sorry, the page you've requested does not exist.{% endtrans %}</p>
</div>
{% endblock %}

View file

@ -1,8 +0,0 @@
{% extends 'partials/layout.html' %}
{% block pageContent %}
<div id="error-page" class="box 503">
<h2>503 - {% trans %}Service Unavailable{% endtrans %}</h2>
<p>{% trans %}Don't panic! This site is down due to maintenance. We will be back soon.{% endtrans %}</p>
</div>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'partials/layout.html' %}
{% block pageContent %}
<div id="error-page" class="box">
<h2>{{ "%d" | format(exc.status_code) }} - {{ phrase }}</h2>
<p>{{ exc.detail }}</p>
</div>
{% endblock %}

View file

@ -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"<h1>{exc.status_code} {phrase}</h1><p>{exc.detail}</p>"
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