How to capture exception in django rest framework with django middleware? - python

Django middleware have a process_exception hook which can be used to capture exception and handler.
But there is some problem while using Django restframe work
class ExceptionHandler(MiddlewareMixin):
#staticmethod
def process_exception(request, exception):
if isinstance(exception, ValidationError):
return Response(data=exception.messages, status=status.HTTP_400_BAD_REQUEST)
For example, I try to use above middleware to capture the ValidationError and return HTTP 400
But it will not work and raise below error
AssertionError: .accepted_renderer not set on Response
It turns out that the rest-framework view layer will add a .accepted_renderer to the response.
If I handle the exception outside view. This attribute will be missed and cause another exception.
So my question is: Is it wrong to handle exception in middleware when using django rest-framework?
What is the correct way to do ?

A better way to do this in Django Rest framework is to create a custom exception handler and replace the default exception handler with your custom handler. For more details on it you can check out the official documentation: http://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling

Related

How to capture middleware exception from other middleware in Django

I coding backend system with Django now, and I want control all exception from Django, so I create one middleware which name is CustomExceptoinMiddleware to control exception.
But sometimes other middleware also raise exception, I hope CustomExceptoinMiddleware can capture it too, but I don't know how to do it.
Can somebody help me?
Thanks in advance!
Python version: 3.7
Django version: 2.2.3
Setting.py
MIDDLEWARE = [
...
"api.core.middleware.CustomExceptoinMiddleware ",
"api.core.middleware.RaiseExcceptionMiddleware",
...
]
# middleware.py
class CustomExceptoinMiddleware(MiddlewareMixin):
def process_exception(self, request, exception):
print(f"Capture exception: {type(exception)}")
class RaiseExcceptionMiddleware(MiddlewareMixin):
def process_request(self, request):
raise KeyError()
You cannot do that. As you may read in documentation (https://docs.djangoproject.com/en/2.2/topics/http/middleware/#process-exception) you can only catch exception from the view. More detailed:
Again, middleware are run in reverse order during the response phase,
which includes process_exception. If an exception middleware returns a
response, the process_exception methods of the middleware classes
above that middleware won’t be called at all.

Django Rest Framework : How to initialise & use custom exception handler?

DRF newbie here.
I'm trying to handle all exceptions within the project through a custom exception handler. Basically, what I'm trying to do is if any serializer fails to validate the data, I want to send the corresponding error messages to my custom exception handler and reformat errors accordingly.
I've added the following to settings.py.
# DECLARATIONS FOR REST FRAMEWORK
REST_FRAMEWORK = {
'PAGE_SIZE': 20,
'EXCEPTION_HANDLER': 'main.exceptions.base_exception_handler',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication'
)
}
But once I send an invalid parameter to any of the endpoints in the project, I still get a default error message of the DRF validator. (e.g. {u'email': [u'This field is required.']})
Errors raised on corresponding serializer's validate function, never reaches to my exception handler.
Here is an image of the Project Tree that I'm working on.
Am I missing something?
Thank you in advance.
To do that, your base_exception_handler should check when a ValidationError exception is being raised and then modify and return the custom error response.
(Note:
A serializer raises ValidationError exception if the data parameters are invalid and then 400 status is returned.)
In base_exception_handler, we will check if the exception being raised is of the type ValidationError and then modify the errors format and return that modified errors response.
from rest_framework.views import exception_handler
from rest_framework.exceptions import ValidationError
def base_exception_handler(exc, context):
# Call DRF's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# check that a ValidationError exception is raised
if isinstance(exc, ValidationError):
# here prepare the 'custom_error_response' and
# set the custom response data on response object
response.data = custom_error_response
return response

How can I use rest serializer.ValidationError in django forms?

I am new to Django and rest_framework. I have a password complexity rules script for 'new user' page.
If my complexity requirements satisfy the needs, it will return true. Else it raises serialiser.ValidationError.
I duplicated Django's forget password mechanism to apply my password rules.
When it raises an error, application crashes like below.
Exception Type: ValidationError
Exception Value:
[u"The two password fields didn't match."]
Is it possible to use serializer errors as form errors {{ form.new_password1.errors }}?
It's possible to write your own custom exception handler to return the error in a preferred format.
The official documentation, at the bottom of the page, says:
The ValidationError class should be used for serializer and field validation, and by validator classes. It is also raised when calling
serializer.is_valid with the raise_exception keyword argument:
The generic views use the raise_exception=True flag, which means that you can override the style of validation error responses globally
in your API. To do so, use a custom exception handler, as described
above.
serializer.is_valid(raise_exception=True)
By default this exception results in a response with the HTTP status
code "400 Bad Request"
Please read here to create your custom handler.

How to catch any exception in python and print stack trace (DJANGO)

I have like this:
try:
#bunch of code
except:
return HttpResponse....
I want to send an error to client and to print stack trace on console. How can i do that?
You can do something like this
import traceback
from django.http import HttpReponse
def view(request):
try:
#throw exception
except:
tb = traceback.format_exc()
return HttpResponse(tb)
# normal flow
You can create a custom middleware class where you can catch all exceptions:
class ErrorMiddleware(object):
def process_exception(self, request, exception):
# send error
return HttpResponse(...) # or None
and put it on first place to MIDDLEWARE_CLASSES tuple in settings.py.
From middleware process_exception docs:
Django calls process_exception() when a view raises an exception.
process_exception() should return either None or an HttpResponse
object. If it returns an HttpResponse object, the template response
and response middleware will be applied, and the resulting response
returned to the browser. Otherwise, default exception handling kicks
in.
You might want to enable logging of all exceptions How do you log server errors on django sites
and make a custom 500 django error page https://docs.djangoproject.com/en/dev/topics/http/views/#the-500-server-error-view
and you should also make a 500 error page at web server level (apache / nginx) just in case the framework doesn't work.
That way you can "catch" all errors and show a nice error message to the client.

Disable Django exception formatting

I'm calling Django from within another application.
While debugging, if a Django exception occurs, the (html) response body is being picked up and then wrapped in an exception generated in the calling app.
This makes it a real pain to determine what the problem was as I need to wade through CSS, Html, Js, etc.
Is there any way to get Django to only output an exception message and a stack trace as plain text?
You can write your own exception-handling middleware class. See the documentation for the process_exception middleware method:
process_exception(self, request, exception)
request is an HttpRequest object. exception is an Exception object raised by the view function.
Django calls process_exception() when a view raises an exception. process_exception() should return either None or an HttpResponse object. If it returns an HttpResponse object, the response will be returned to the browser. Otherwise, default exception handling kicks in.
So I think you need to write something like this:
import traceback
from django.http import HttpResponse
class PlainTextExceptionMiddleware(object):
def process_exception(self, request, exception):
return HttpResponse(traceback.format_exc(), "text/plain")
and then add the class to your MIDDLEWARE_CLASSES setting.
Just set the DEBUG_PROPAGATE_EXCEPTIONS in your settings:
If set to True, Django’s normal exception handling of view functions
will be suppressed, and exceptions will propagate upwards. This
can be useful for some test setups, and should never be used on a live site.
See the Django docs

Categories