Convert HttpError to JSON - django

In my Django app in several places I do:
raise HttpError(
message="Bla bla bla",
status=400,
)
Is there some way to intercept all HttpErrors being raised and wrap them in a nice JSON body? Something like:
{
"status": "error",
"message":" "Bla bla bla"
}

Custom middleware is the place where you should do the job.
Create a middleware class, define process_exception() method; in case of HttpError return a json response, otherwise return None (do not re-raise the exception):
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.
Here's an example:
class CustomMiddleware():
def process_exception(self, request, exception):
if not isinstance(exception, HttpError):
return None
response = json.dumps({'status': exception.status,
'message': exception.message})
return HttpResponse(response,
content_type='application/json; charset=utf-8')
Also, don't forget to add your middleware to MIDDLEWARE_CLASSES setting.
Hope that helps.

Create or find middleware that will catch the exception and check the URI or view to see if it should convert the exception or reraise it.

Related

Global Exception Handling in Django-rest-framework

Is there a way to handle all the exceptions globally without using try-except block in django rest framework.
I want to convert html error page that django is throwing to a customised json object response.
I have created an exception.py file in my app
def custom_exception_handler(exc, context=None):
response = exception_handler(exc)
if isinstance(exc, HttpResponseServerError):
custom_response_data = {
'detail': 'Internal Server Error' # custom exception message
}
response.data = custom_response_data
return response
i have configured this in settings.py.
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'EXCEPTION_HANDLER':'my_project.my_app.exceptions.custom_exception_handler'}
Since I came across with a similar situation that lead me to this question, I'll answer following the original question that is related to Django Rest Framework specifically and not just Django.
I understand that you want to handle raised exceptions from your views, globally, without having to define try/except blocks on each view module.
DRF allows you to define your own Custom Exception Handling mechanism (docs).
Here is an example:
At my_custom_except_handler.py:
import logging
from rest_framework.views import exception_handler
from django.http import JsonResponse
from requests import ConnectionError
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first
response = exception_handler(exc, context)
# checks if the raised exception is of the type you want to handle
if isinstance(exc, ConnectionError):
# defines custom response data
err_data = {'MSG_HEADER': 'some custom error messaging'}
# logs detail data from the exception being handled
logging.error(f"Original error detail and callstack: {exc}")
# returns a JsonResponse
return JsonResponse(err_data, safe=False, status=503)
# returns response as handled normally by the framework
return response
As stated in the docs, the defined response object refers to:
The exception handler function should either return a Response object, or return None if the exception cannot be handled. If the handler returns None then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.
In other words, 'response' won't be None only when handling these exceptions docs:
Subclasses of APIException.
Django's Http404 exception.
Django's PermissionDenied exception.
If your custom handler returns None, then the exception will be handled 'normally' by the framework, returning typical 500 server error.
Finally remember to set the required key at settings.py:
REST_FRAMEWORK = {'EXCEPTION_HANDLER':
'my_project.my_app.my_custom_except_handler.custom_exception_handler'}
Hope it helps!
The definite answer to your question is no.
At least I don't know how to do it globally in Django, whereas global includes middleware exceptions).
Further, as request by #Shubham Kumar, the hook you need is process_exception and for an implementation check this SO post with the offical docs on how to activate it.
As stated in the Django docs:
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 template response and response middleware will be applied and the resulting response returned to the browser. Otherwise, default exception handling kicks in.
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.
Meaning that you will merely be able to hook into the view function and catch all those exceptions.

raise exception in django oauth toolkit

I'm using Django 2.x and django-oauth-toolkit to generate access token.
I have written a custom token view to run a few checks on the account and then generate access token to the user. If custom check fails, I want to raise an exception with 400 status code.
class CustomTokenView(TokenView):
def create_token_response(self, request):
login = request.POST.pop('username', None)
username = get_user_model().objects.filter(
email=login[0]
).last()
if not username.verified:
raise HttpResponse(content='User not verified', status=status.HTTP_400_BAD_REQUEST)
request.POST._mutable = mutable
return super(TokenView, self).create_token_response(request)
But this gives error as
TypeError: exceptions must derive from BaseException
I also tried with
from rest_framework.response import Response
return Response('User not verified', status=status.HTTP_400_BAD_REQUEST)
But none is working.
You cannot raise a response. Response is not an exception. Instead you can either return it or raise an actual exception from django-rest-framework (all available exceptions described here, select one that suits best your case. In my opinion it should be your custom one, created from APIException).

Over-ride DRF custom exception response

I want to send error_codes while sending the response back to the client, in case of an error!!
So, I've a form where params a and b is required. If any of the param is not POSTed, then DRF serializer sends back a response saying This field is required. I want to add the error code to the response as well for the client to identify. Different errors different error codes.
So, I wrote my own custom exception handler. It goes like this.
response = exception_handler(exc, context)
if response is not None:
error = {
'error_code': error_code, # Need to identify the error code, based on the type of fail response.
'errors': response.data
}
return Response(error, status=http_code)
return response
The problem I'm facing is that I need to identify the type of exception received, so that I can send the error_code accordingly. How can I achieve this?
REST framework's views handle various exceptions, and deal with returning appropriate error responses.
The handled exceptions are:
APIException raised inside REST framework.
Django's Http404
Django's PermissionDenied exception.
You can identify the type of exception received by status
from rest_framework import exceptions, status
response = exception_handler(exc, context)
if response is not None:
if response.status == status.HTTP_404_NOT_FOUND:
# Django's Http404
elif response.status == status.HTTP_403_FORBIDDEN:
# PermissionDenied exception
else:
# APIException raised
Also most error responses will include a key detail in the body of the response.
You can check it and set custom error codes.
response = exception_handler(exc, context)
if response is not None:
# identify the type of exception received, detail can be text, list or dictionary of items.
if response.data.detail == 'some text':
error_code = 111
elif more_conditions:
...
Reference: http://www.django-rest-framework.org/api-guide/exceptions/

Redirect Middleware not working - Django

I created a simple redirect middleware
class RedirectMiddleware(object):
def urlredirect(self, request):
path = self.request.build_absolute_uri()
if "something" in path:
URL = "http://www.someurl.com"
else:
URL = "http://www.otherurl.com"
return HttpResponsePermanentRedirect(URL)
But i doesnt seem to work , it doesnt does anything , and yes i added it on the settings.py, any idea ?
You need to implement process_request
Writing your own middleware is easy. Each middleware component is a
single Python class that defines one or more of the following methods:
process_request
process_request(request)
request is an HttpRequest object.
process_request() is called on each request, before Django decides
which view to execute.
It should return either None or an HttpResponse object. If it returns
None, Django will continue processing this request, executing any
other process_request() middleware, then, process_view() middleware,
and finally, the appropriate view. If it returns an HttpResponse
object, Django won’t bother calling any other request, view or
exception middleware, or the appropriate view; it’ll apply response
middleware to that HttpResponse, and return the result.

getting access to the exception in Django 500.html?

How can I get access to the exception details in 500.html?
Easiest way is to write a middleware that overrides process_exception.
http://docs.djangoproject.com/en/dev/topics/http/middleware/#process-exception
class ProcessExceptionMiddleware(object):
def process_exception(self, request, exception):
response = direct_to_template(request, "my_500_template", {'exception': exception})
response.status_code = 500
return response
You can subclass django.core.handlers.base.BaseHandler, or better one of the implementations like django.core.handlers.wsgi.WSGIHandler, and change the handle_uncaught_exception(self, request, resolver, exc_info) method. The last argument is the exception info as returned by sys.exc_info. In the case of WSGI, you would define the custom handler in your WSGI file, for instance.
Simply overwriting handler500 in your URLconf won't work because that function does not receive any information about the actual exception.