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.
Related
I want to write django custom error handler as in flask custom error handler .
Suppose i have 100 api's which gets the same error everytime let's say
json.decoder.JSONDecodeError
Sample code
def post(self, request):
if not request: return Response({"message": "Please enter credentials"})
input_param = json.load(request)
print(input_param)
return "something"
The above code will return json.decoder.JSONDecodeError if no params are passed in post request .
In flask this can be handled by writing custom error handler like
#app.errorhandler(json.decoder.JSONDecodeError)
def handle_marshmallow_validaton_errors(err):
return jsonify({"error": "Bad request"}), 400
In django is there any way we can write custom error handlers
Thanks in advance
I think you can use DRF custom exception handler https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling.
you can use it to check the spawn exception and return a proper response.
During development, I am running Django in Debug mode and I am posting data to my application using a text mode application. Ideally, I need to receive a plain text response when I get an http error code 500 so I don't have to look for the real error inside all that HTML and Javascript.
Is it possible to obtain a Django 500 Internal Server Error as plain text?
If you are looking for a way to get a plain text error page when using curl, you
need to add the HTTP header X-Requested-With with value XMLHttpRequest, e.g.
curl -H 'X-Requested-With: XMLHttpRequest' http://example.com/some/url/
Explanation: this is because Django uses the is_ajax method to determine whether or not to return as plain text or as HTML. is_ajax in turn looks at X-Requested-With.
Update
Since django version 3.1, error reporting ignores the X-Requested-With header. Instead, set the Accept header on the request to any valid value which does not include the text/html mime type.
e.g.
curl -H 'Accept: application/json;charset=utf-8' http://example.comp/some/url
I think to write a middleware, because otherwise the exception isn't available in the 500.html
http://docs.djangoproject.com/en/dev/topics/http/middleware/#process-exception
class ProcessExceptionMiddleware(object):
def process_exception(self, request, exception):
t = Template("500 Error: {{ exception }}")
response_html = t.render(Context({'exception' : exception }))
response = http.HttpResponse(response_html)
response.status_code = 500
return response
There's a setting DEBUG_PROPAGATE_EXCEPTIONS which will force Django not to wrap the exceptions, so you can see them, e.g. in devserver logs.
This is an improvement on Yuji's answer, which provides a stacktrace, more instructions (for us django newbies) and is simpler.
Put this code in a file somewhere in your application, e.g. PROJECT_ROOT/MAIN_APP/middleware/exceptions.py, and make sure you have an empty __init__.py in the same directory.
import traceback
from django.http import HttpResponse
class PlainExceptionsMiddleware(object):
def process_exception(self, request, exception):
return HttpResponse(traceback.format_exc(exception), content_type="text/plain", status=500)
Now edit your settings.py and find MIDDLEWARE_CLASSES = (. Add another entry so it is like this:
MIDDLEWARE_CLASSES = (
# (all the previous entries)
# Plain text exception pages.
'MAIN_APP.middleware.exceptions.PlainExceptionsMiddleware',
)
Restart django and you are good to go!
User-agent aware formatting.
If you're like me and developing an app and a website both backed by django, you probably want to show plain text error pages to the app, and the nice formatted ones to the browser. A simple way to to that is to check the user agent:
import traceback
from django.http import HttpResponse
class PlainExceptionsMiddleware(object):
def process_exception(self, request, exception):
if "HTTP_USER_AGENT" in request.META and "chrome" in request.META["HTTP_USER_AGENT"].lower():
return
return HttpResponse(traceback.format_exc(exception), content_type="text/plain", status=500)
Building off of Timmmm's answer, I had to make several modifications for it to work in Django 3.1:
Create a file somewhere in your application, such as YOUR_APP_NAME/middleware/exceptions.py and paste the following code:
import traceback
from django.http import HttpResponse, HttpRequest
class PlainExceptionsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request: HttpRequest, exception: Exception):
if "HTTP_USER_AGENT" in request.META and "chrome" in request.META["HTTP_USER_AGENT"].lower():
return
print(traceback.format_exc())
return HttpResponse(repr(exception), content_type="text/plain", status=500)
It is not necessary to create an __init__.py file in the middleware folder.
In settings.py, add the following item to the end of the MIDDLEWARE variable, so that it looks like:
MIDDLEWARE = [
# ...
'YOUR_APP_NAME.middleware.exceptions.PlainExceptionsMiddleware'
]
Now, if "HTTP_USER_AGENT" and "chrome" are in the request header, this middleware doesn't do anything, so Django returns an HTML response as usual. Otherwise, it returns a plain-text representation of the error as a response (e.g., ValueError("Field 'id' expected a number but got 'undefined'.")) and prints out the traceback to the Django console, as Django normally would. Of course, you can instead return the full traceback as your response.
Is it possible to raise BadRequest as exception in django?
I have seen that you can raise a 404 [1].
Use case: in a helper method I load a json from request.GET. If the json was cut since the browser (IE) cut the url, I would like to raise a matching exception.
A BadRequest exception looks appropriate, but up to now there seems to no such exception in django.
In 1.6 there is a SuspiciousOperation exception. But this does not match in my case, since it is not security related.
Of course I could put a try..except around my helper method in the view method, but this is not DRY.
Has someone a solution where I don't need a try..exception around every call of my helper method?
[1] https://docs.djangoproject.com/en/1.6/ref/exceptions/#django.core.urlresolvers.Resolver404
Update
Code example:
def my_view(request):
data=load_data_from_request(request) # I don't want a try..except here: DRY
process_data(data)
return django.http.HttpResponse('Thank you')
def load_data_from_request(request):
try:
data_raw=json.loads(...)
except ValueError, exc:
raise BadRequest(exc)
...
return data
The other answers are explaining how to return an HTTP response with 400 status.
If you want to hook into Django's 400 error handling, you can raise a SuspiciousOperation exception or a subclass of it.
See the docs here and here.
In your example it would look like:
from django.core.exceptions import SuspiciousOperation
def load_data_from_request(request):
try:
data_raw = json.loads(...)
except ValueError:
raise SuspiciousOperation('Invalid JSON')
# ...
return data
You need custom middleware to handle exception what you raise.
Utilize custom exceptions to check for this condition in middleware.
class ErrorHandlingMiddleware(object):
def process_exception(self, request, exception):
if not isinstance(exception, errors.ApiException): # here you check if it yours exception
logger.error('Internal Server Error: %s', request.path,
exc_info=traceback.format_exc(),
extra={
'request': request
}
)
# if it yours exception, return response with error description
try:
return formatters.render_formatted_error(request, exception) # here you return response you need
except Exception, e:
return HttpResponseServerError("Error During Error Processing")
As an alternative to #coldmind's answer (converting exceptions in a middleware layer), you could put a decorator on your view function which does the same thing. Personally I prefer this, because it's just plain-old-Python, and doesn't require me dust off my knowledge of how Django middleware works.
You don't want to stream-of-conciousness inline all functionality in your view functions (this makes your view module depend on all your project's other modules, leading to 'everything depends on everything else' architecture) Instead, it's better if the view just knows about http. It extracts what you need from the request, delegates to some other 'business logic' function. The business logic might delegate to other modules (e.g. database code or interfaces to other external systems.) Then finally the return value from your business logic is converted into an http response by the view function.
But how to communicate errors back to the view function from the business logic (or whatever it delegates to)? Using return values is irksome for many reasons. For example, these error return values will have to be propogated back to the view from all through your whole codebase. This is often cripplingly messy because you will already be using the return values of functions for other purposes.
The natural way to deal with this is to use exceptions, but the Django view won't, by itself, convert uncaught exceptions into returned HTTP status codes (except for a couple of special cases, as the OP says.)
So. I write a decorator to apply to my view. The decorator converts various raised exception types into different returned django.http.HttpResponseXXX values. e.g:
# This might be raised by your business logic or database code, if they get
# called with parameters that turn out to be invalid. The database code needs
# know nothing about http to do this. It might be best to define these exception
# types in a module of their own to prevent cycles, because many modules
# might need to import them.
class InvalidData(Exception):
pass
# This decorator is defined in the view module, and it knows to convert
# InvalidData exceptions to http status 400. Add whatever other exception types
# and http return values you need. We end with a 'catch-all' conversion of
# Exception into http 500.
def exceptions_to_http_status(view_func):
#wraps(view_func)
def inner(*args, **kwargs):
try:
return view_func(*args, **kwargs)
except InvalidData as e:
return django.http.HttpResponseBadRequest(str(e))
except Exception as e:
return django.http.HttpResponseServerError(str(e))
return inner
# Then finally we define our view, using the decorator.
#exceptions_to_http_status
def myview(self, request):
# The view parses what we need out of incoming requests
data = request.GET['somearg']
# Here in the middle of your view, delegate to your business logic,
# which can just raise exceptions if there is an error.
result = myusecase(data)
# and finally the view constructs responses
return HttpResponse(result.summary)
Depending on circumstance, you might find the same decorator could work on many, or all, of your view functions.
HttpResponseBadRequest is ready to use. It is implemented as:
class HttpResponseBadRequest(HttpResponse):
status_code = 400
Edited due OP updated question.
You can create your own helper and encapsulate try-catch block into it.
def myJsonDec(str):
try:
...
Since 3.2 Django provides BadRequest class. So you can now do
try:
data_raw = json.loads(...)
except ValueError:
raise BadRequest('Invalid JSON')
The problem with that is that for some reason the message 'Invalid JSON' doesn't appear in the error response, only generic one is shown:
<!doctype html>
<html lang="en">
<head>
<title>Bad Request (400)</title>
</head>
<body>
<h1>Bad Request (400)</h1><p></p>
</body>
</html>
I'm not sure what you mean by raising BadRequest as an exception.
You can return a response with any status code you like, either by explicitly using the relevant subclass of HttpResponse, or by adding a status parameter to the normal response.
I think one of the simple way is to define your own BadRequestException and raise it in called function.
from django.http import HttpResponseBadRequest, HttpResponse
class BadRequestException(Exception):
def __init__(self, message='', *args, **kwargs):
self.message = message
def my_view(request):
try:
data = get_data_from_another_func()
except BadRequestException as e:
return HttpResponseBadRequest(e.message)
process_data(data)
return HttpResponse('Thank you')
def get_data_from_another_func():
raise BadRequestException(message='something wrong')
def process_data(data):
pass
The simpler solution I found is to raise the BadRequest exception. Here is a method I use to return Http400 if the required parameter is not passed in the request. Raising the BadRequest exception will make the request return a 400 status code.
from django.core.exceptions import BadRequest
from django.http import HttpResponse
from django.views import View
def get_param_or_return_400(kwargs: dict, key: str):
if key in kwargs:
return kwargs[key]
raise BadRequest(f'The parameter "{key}" must be provided')
class MyView(View):
def post(self, request, *args, **kwargs):
query = get_param_or_return_400(kwargs, 'query')
# At this point we know query is not None
do_some_operation(query)
return HttpResponse('OK')
Django-Tastypie has ImmediateHttpResponse exception which allow to return to the client an immediate response:
raise ImmediateHttpResponse(response='a message')
Django has Http404, but i couldn't find a more universal exception like ImmediateHttpResponse.
What technique do you use to return to client an immediate 400 response?
For example having model:
class Subscriber(Model):
def delete(self, *args, **kwargs):
raise ImmediateHttpResponse('Deleting subcribers is not allowed!')
and trying to delete an object would return to the client a 400 response with the given message.
I think what you want is a middleware which implements a process_exception.
It works like this: you raise an Exception on you view (e.g. ImmediateHttpResponse). If the exception is catched by your middleware, the middleware returns a response, in your case the with a status 400. If you don't catch the exception, Django will catch it in the end, returning a status 500 (server error), or other status.
The simplest example is a middleware that catches Http404. If you catch the exception Http404 in your middleware, you can return any response you want (status 200, 400, etc). If you don't catch (i.e. the process_exception method of you middleware returns None), Django will catch it for you and return a Response with status 404. This is actually the standard way of having your own custom exceptions that you want to respond in a custom way.
It is not an exception, but there is HttpResponseBadRequest, which is your normal HttpResponse but with 400.
The Http404 exception is simply an empty Exception class, there is nothing special about it:
class Http404(Exception):
pass
So you can easily create your own if you must:
class Http400(Exception):
pass
ImmediateHttpResponse isn't that much different than Http404 in that its also a generic Exception but with a specific property, which makes it more like HttpResponseBadRequest:
class ImmediateHttpResponse(TastypieError):
"""
This exception is used to interrupt the flow of processing to immediately
return a custom HttpResponse.
Common uses include::
* for authentication (like digest/OAuth)
* for throttling
"""
_response = HttpResponse("Nothing provided.")
def __init__(self, response):
self._response = response
#property
def response(self):
return self._response
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