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/
Related
I have a DRF backend with a Vue frontend. It has been working for a while (alpha test), but I am trying to thoroughly test the authentication of the site.
I login as a test user, then go directly to the database and delete that user from the authtoken_token table. I then hit a backend endpoint to see what error I get.
I'm getting an AuthenticationFailed message, but it is an HTTP response, not JSON. Also, it is a "500 Internal Server Error" instead of a 401 or 403.
Looking at the handle_exception() method (rest_framework/views.py), I see that it is checking for NotAuthenticated and AuthenticationFailed exceptions, so it should match the actual exception of AuthenticationFailed.
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request)
if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
exception_handler = self.get_exception_handler()
context = self.get_exception_handler_context()
response = exception_handler(exc, context)
if response is None:
self.raise_uncaught_exception(exc)
response.exception = True
return response
How to ensure I get a JSON response?
I want to create custom exceptions in DRF. As the DRF documentation states that i can do that by creating a user defined class and inheriting from APIException.
That works well but the issue is my application require me to send some type of custom codes along with the HTTP status codes.
For example, when an error occurred while creating a ProductCatalog it should return something like this along with HTTP status code.
{"code": 4026, "message": "Unable to create catalog"}
Such thing is required because although my API worked correctly so the http status code will be 200 but there were some business logic that was not fulfilled hence i need to return some sort of custom code and message to let the client application handle it accordingly.
Any kind of help will be appreciated.
You can consider creating a custom exception handler, along with a custom exception. Like this:
First create exception handler which will handle errors for DRF:
# custom handler
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.default_code
return response
Then update the settings.py with that error
# settings.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
Finally, create a custom exception and use that when you need to raise Catalog creation error:
# Custom exception
from rest_framework.exceptions import APIException
class CatalogExeption(APIException):
status_code = 503
default_detail = 'Unable to create catalog'
default_code = '4026'
More information can be found regarding custom exception in documentation.
I figured it out myself. By going through the code i found that if you set default_detail to a dictionary, it will return that as it is.
In my case it would be something like this.
class ProductCatalogExeption(APIException):
status_code = 200 #or whatever you want
default_code = '4026'
# Custom response below
default_detail = {"code": 4026, "message": "Unable to create catalog"}
So when ProductCatalogException is raised it will return
{"code": 4026, "message": "Unable to create catalog"}
with HTTP Response code 200
For reference: https://github.com/encode/django-rest-framework/blob/master/rest_framework/exceptions.py
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.
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).
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.