ghazi-git/drf-standardized-errors

DRF returning HTML on 404 even with drf-standardized-errors installed

Closed this issue · 5 comments

I've noticed that the standardized errors don't work if the route doesn't exist. I've made a minimum reproducible example based on the DRF tutorial, which successfully returns the standardized error if you ask for a primary key that doesn't exist:

➜  curl -H "Accept: application/json" "http://127.0.0.1:8000/users/-1/"
{"type":"client_error","errors":[{"code":"not_found","detail":"Not found.","attr":null}]}%

but still returns html if you ask for a route that doesn't exist

➜  curl -H "Accept: application/json" "http://127.0.0.1:8000/user/2/"

<!doctype html>
<html lang="en">
<head>
  <title>Not Found</title>
</head>
<body>
  <h1>Not Found</h1><p>The requested resource was not found on this server.</p>
</body>
</html>

mre.zip

Thanks for raising the issue @scottgigante and providing a sample project.

The problem is that requests to non-existing routes do not reach drf, they are handled by django. So, the drf-standardized-errors exception handler of is never called.

To return the expected format, you can create a custom handler404 that returns the json you want.

This issue got me thinking: what should drf-standardized-errors do about error responses returned by django without reaching drf? like this one or those that happen in middleware... At the very least, some documentation is in order. Maybe provide default error handlers (400,403,404,500) matching what django provides out of the box?

That would be great! On my end it appears this works -- though I'm not entirely sure as I'm not certain how to trigger core Django exceptions for anything other than 404 and 500.

# project/app/exceptions.py
import drf_standardized_errors.handler
from rest_framework.decorators import api_view
from rest_framework.exceptions import APIException

@api_view()
def error_handler(request, exception=APIException()):
    """
    Generic Django error handler.
    """
    return drf_standardized_errors.handler.exception_handler(exception, {'request': request})

and then

# project/project/urls.py
handler400 = "app.exceptions.error_handler"
handler403 = "app.exceptions.error_handler"
handler404 = "app.exceptions.error_handler"
handler500 = "app.exceptions.error_handler"

reusing the exception handler in the error handler seems like a nice idea. For now, there is a small issue: for 500 errors, you might see duplicate errors in sentry and duplicate error logs. That's because the handler sends the got_request_exception and django also does that as well, and sentry relies on that to report errors

Good point. Not a huge issue but a little suboptimal. I'd be interested if you come up with something better!

I went ahead and documented this issue in the readme and linked to this issue.

I'm closing the issue as resolved as the solution is up to the developer. Scott's answer is a good start assuming the project provides a backend API only.