i am working with django rest framework and want to raise a PermissionDenied exception and return a 403 response to user:
raise PermissionDenied("Custom exception message")
But it returns a 500 status code...
Can anybody explain?
thanks!
Related
I am working on a simple Flask REST API test and when I call the {{url}}/items for example I get the items list. However if a call is passed to an endpoint that does not exist for example {{url}}/itemsss then I get the error 404 in html.
I would like to make the error handling more friendly and return json instead of html for certain errors such as 400, 404,405...
For the 404 for example i tried this:
#app.errorhandler(404)
def not_found(e):
response = jsonify({'status': 404,'error': 'not found',
'message': 'invalid resource URI'})
response.status_code = 404
return response
However it does not work.
My issue is similar to this one: Python Flask - Both json and html 404 error
I wanted to know, if using the blueprints the only way to accomplish this?
If there a simpler way to output the 404 error as json?
For example instead of this:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
Something like this:
{
error: true,
status: 404,
code: "error.notFound",
message: "API endpoint not found",
data: { }
}
I appreciate your help with this.
Usually when I need to return a custom error message with Flask-RESTful I would do something like:
from flask import make_response, jsonify
def custom_error(message, status_code):
return make_response(jsonify(message), status_code)
I think I found the solution in the official documentation:
from flask import json
from werkzeug.exceptions import HTTPException
#app.errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
# start with the correct headers and status code from the error
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response
Code and description are there in HTTP anyways, need only the message. And using jsonify from flask.
from flask import jsonify
from werkzeug.exceptions import HTTPException
#app.errorhandler(HTTPException)
def handle_exception(e):
return jsonify({"message": e.description}), e.code
I made custom 404 page in django. And I'm trying to get 404 error page intentionally.
myproject/urls.py:
from website.views import customhandler404, customhandler500, test
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^test/$', test, name='test'),
]
handler404 = customhandler404
handler500 = customhandler500
website/views.py
def customhandler404(request):
response = render(request, '404.html',)
response.status_code = 404
return response
def customhandler500(request):
response = render(request, '500.html',)
response.status_code = 500
return response
def test(request):
raise Http404('hello')
But when I go 127.0.0.1:8000/test/ , It seems to return 500.html
And terminal says:
[24/Mar/2018 22:32:17] "GET /test/ HTTP/1.1" 500 128
How can I intentionally get 404 page?
When you set debug to False, you don't have a custom handler, and the status code of the response is 404, the 404.html (if present) in your base template directory is used. To return a response with a 404 status, you can simply return an instance of django.http.HttpResponseNotFound. The reason you got a 500 is because you raised an error instead of returning a response. So, your test function can be simply modified to this
from django.http import HttpResponseNotFound
def test(request):
return HttpResponseNotFound("hello")
Update:
So it turned out that the reason you are getting a 500 error was not that you raised an exception, but having incorrect function signatures. When I answered this question more than half a year ago I forgot that django catches HTTP404 exception for you. However, the handler view has different signatures than the normal views. The default handler for 404 is defaults.page_not_found(request, exception, template_name='404.html'), which takes 3 arguments. So your custom handler should actually be
def customhandler404(request, exception, template_name='404.html'):
response = render(request, template_name)
response.status_code = 404
return response
Although, in this case, you may as well just use the default handler.
I want to return a HTTP 400 response from my django view function if the request GET data is invalid and cannot be parsed.
How do I do this? There does not seem to be a corresponding Exception class like there is for 404:
raise Http404
From my previous comment :
You can return a HttpResponseBadRequest
Also, you can create an Exception subclass like Http404 to have your own Http400 exception.
You can do the following:
from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation("Invalid request; see documentation for correct paramaters")
SuspiciousOperation is mapped to a 400 response around line 207 of https://github.com/django/django/blob/master/django/core/handlers/base.py
If you're using the Django Rest Framework, you have two ways of raising a 400 response in a view:
from rest_framework.exceptions import ValidationError, ParseError
raise ValidationError
# or
raise ParseError
Since Django 3.2, you can also raise a BadRequest exception:
from django.core.exceptions import BadRequest
raise BadRequest('Invalid request.')
This may be better in some cases than SuspiciousOperation mentioned in another answer, as it does not log a security event; see the doc on exceptions.
Since moving to Django 1.4 with DEBUG = True and TEMPLATE_DEBUG = True, I no longer see the typical yellow error screen with traceback when I encounter an error locally. Instead I get a plain white screen that says, "An error has occurred. Please check your logs . . . ". Is this the new behavior or have I screwed something up by combining 1.3 and 1.4 files and settings.
Here's an example of my local settings.
The error handler
is located in django.core.handler.base try to debug the handle_uncaught_exception function.
def handle_uncaught_exception(self, request, resolver, exc_info):
"""
Processing for any otherwise uncaught exceptions (those that will
generate HTTP 500 responses). Can be overridden by subclasses who want
customised 500 handling.
Be *very* careful when overriding this because the error could be
caused by anything, so assuming something like the database is always
available would be an error.
"""
from django.conf import settings
if settings.DEBUG_PROPAGATE_EXCEPTIONS:
raise
logger.error('Internal Server Error: %s', request.path,
exc_info=exc_info,
extra={
'status_code': 500,
'request': request
}
)
if settings.DEBUG:
from django.views import debug
check if you pass here
return debug.technical_500_response(request, *exc_info)
# If Http500 handler is not installed, re-raise last exception
if resolver.urlconf_module is None:
raise exc_info[1], None, exc_info[2]
# Return an HttpResponse that displays a friendly error message.
callback, param_dict = resolver.resolve500()
return callback(request, **param_dict)
check with the debugger that you pass in debug.technical_500_response
I am trying to automate 404 pages testing using Django 1.4's testing framework.
If I print 127.0.0.1:8000/something/really/weird/ in browser address bar with development server running, I see a 404 page, with correct "404 NOT FOUND" status (as firebug shows).
But if I try to use this code for testing:
from django.test import TestCase
class Sample404TestCase(TestCase):
def test_wrong_uri_returns_404(self):
response = self.client.get('something/really/weird/')
self.assertEqual(response.status_code, 404)
the test fails with this output:
$./manage.py test main
Creating test database for alias 'default'...
.F
======================================================================
FAIL: test_wrong_uri_returns_404 (main.tests.Sample404TestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".../main/tests.py", line 12, in test_wrong_uri_returns_404
self.assertEqual(response.status_code, 404)
*AssertionError: 200 != 404*
----------------------------------------------------------------------
Ran 2 tests in 0.031s
FAILED (failures=1)
Destroying test database for alias 'default'...
I'm seriously surprised with getting 200 code here. Anyone have any idea why on earth this is happening?
updated:
here lies urls.py: http://pastebin.com/DikAVa8T
and actual failing test is:
def test_wrong_uri_returns_404(self):
response = self.client.get('/something/really/weird/')
self.assertEqual(response.status_code, 404)
everything is happening in project https://github.com/gbezyuk/django-app-skeleton
Try
response = self.client.get('/something/really/weird/') # note the '/' before something
127.0.0.1:8000/something/really/weird/ is /something/really/weird/ in path relative to root, not
something/really/weird
something/really/weird/
/something/really/weird
The problem is that your ViewFor404 class returns a 200 status code. Look at Django's TemplateView definition:
class TemplateView(TemplateResponseMixin, View):
"""
A view that renders a template.
"""
def get_context_data(self, **kwargs):
return {
'params': kwargs
}
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
so all your class does is a render_to_response, which generates a '200' response.
If you need to override the 404 handler, you should do something more like this in the view:
return HttpResponseNotFound('<h1>Page not found</h1>')
(I don't know the equivalent in class-based views)
Or better yet, can you avoid customizing the View? To customize the 404 display, you can just create a 404.html template (in your site's templates/ directory), and it will be picked up by Django's error viewer.