Skip to content

Refactor: class-based error views - #3439

Merged
thekaveman merged 8 commits into
mainfrom
refactor/cbv-error-views
Jan 26, 2026
Merged

Refactor: class-based error views#3439
thekaveman merged 8 commits into
mainfrom
refactor/cbv-error-views

Conversation

@thekaveman

@thekaveman thekaveman commented Jan 16, 2026

Copy link
Copy Markdown
Member

Closes #3391

It turns out this was pretty straightforward, so instead of writing follow-up tickets to implement each of the error handlers, this PR just does it.

The Django error handlers (e.g. handler400) expect:

A callable... that should be called if the HTTP client has sent a request that caused an error condition and a response with a status code of 400.

For function-based views, we inherently have a callable to assign to these props (the function view itself). But this is also exactly the purpose of a CBV ViewClass.as_view() helper function:

Returns a callable view that takes a request and returns a response

So just like our standard URL path registrations for other CBVs that make use of the .as_view() helper, these error handlers can do the same thing!

There is a minor caveat for 2 of the error handlers:

  • handler500 is very strict about the function signature, and .as_view() doesn't pass this strictness test
  • CSRF_FAILURE_VIEW is defined in settings.py, where we don't want to import a view class (to avoid circular imports)

In both cases, we define a small helper function that simply wraps .as_view().

What this PR does

  • Creates a base CBV for error handlers that:
    • Decorates dispatch with our common Middlewares for ViewedPageEvent and IndexOrAgencyIndexOrigin to reduce duplication
    • Sets a status_code attribute for the eventual HttpResponse
    • Inherits from TemplateView and optionally calculates the template_name from the status_code for easier inheritance
  • Refactors all built-in Django error handler overrides to CBVs using the base CBV
  • Moves all error handler CBVs to the top-level benefits.views
  • Adds paths (in local only) for testing error handlers

Reviewing

  1. Checkout this branch, rebuild and reopen the devcontainer
  2. Start the app without debugging
  3. Check that each of the following paths return the expected error template, and that the browser console shows the corresponding status code
    • /test400 (400)
    • /test403 (403)
    • /test404 (404)
    • /test500 (500)
    • /testcsrf (click to submit the form and trigger the handler - 403)

status_code attribute used by child classes to ensure the correct response

infer template_name from status_code if not provided

apply common method decorators in dispatch
@github-actions github-actions Bot added back-end Django views, sessions, middleware, models, migrations etc. tests Related to automated testing (unit, UI, integration, etc.) and removed back-end Django views, sessions, middleware, models, migrations etc. labels Jan 16, 2026
@github-actions

github-actions Bot commented Jan 16, 2026

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  benefits
  settings.py
  urls.py 57, 60, 63, 66, 69-71
  views.py
  benefits/core
  middleware.py
  urls.py
  views.py
Project Total  

This report was generated by python-coverage-comment-action

function handler still required for CSRF_FAILURE_VIEW setting
function handler still required for handler500 override
only active in the local environment
BaseErrorView needs to implement post to avoid a 405 Method Not Allowed
and actually trigger the CSRF error handler
we should return the correct error code here instead of 400
@thekaveman
thekaveman force-pushed the refactor/cbv-error-views branch from 821865f to 5c97744 Compare January 16, 2026 17:56
@thekaveman
thekaveman marked this pull request as ready for review January 16, 2026 17:57
@thekaveman
thekaveman requested a review from a team as a code owner January 16, 2026 17:58
@thekaveman thekaveman added chore Internal maintenance or code quality work back-end Django views, sessions, middleware, models, migrations etc. labels Jan 16, 2026

@lalver1 lalver1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good! While in general the required changes turned out to be relatively straightforward, there's a good bit going on in this PR (for example CSRF failures and Django specific requirements on the function signature of a view) so thanks for the notes 👍 they helped a lot with the review! I also tested locally and saw the expected templates/response codes.

I only had a general question about the templates that are shown for each type of HTTP error. For responses that return 400 and 403 errors, we return the 400.html template which alludes to the service for the site being down. I think the text in the template is meant to be generic to cover a bad request (400), a request for a forbidden resource (403), and a request that is rejected by the CSRF protection (403), but for most of these cases the issue would not originate with Benefits but with the client (browser) itself, correct?

@thekaveman

Copy link
Copy Markdown
Member Author

For responses that return 400 and 403 errors, we return the 400.html template which alludes to the service for the site being down. I think the text in the template is meant to be generic

Yes I think this is correct, we wanted something more generic to let the user know something is wrong, without trying to diagnose exactly what might be wrong and provide potentially misleading or inaccurate information. This all came out of the error pages work that Sarah initially did (maybe 2 years ago? Or more...)

Since we didn't attempt change the actual language in this PR, I think we just leave everything as-is and perhaps review in another round with @indexing and @cmajel, maybe in concert with some ideas we've had around user-facing outage alerts/notifications.

@thekaveman
thekaveman merged commit 3260045 into main Jan 26, 2026
14 checks passed
@thekaveman
thekaveman deleted the refactor/cbv-error-views branch January 26, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

back-end Django views, sessions, middleware, models, migrations etc. chore Internal maintenance or code quality work tests Related to automated testing (unit, UI, integration, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Class-based views: evaluate refactor for Django error handler views

2 participants