From 59c3ebc6dddab2a52b5c4079fb78871b35b5dcaa Mon Sep 17 00:00:00 2001 From: Adrian Holovaty <adrian@holovaty.com> Date: Fri, 5 Aug 2005 22:22:41 +0000 Subject: [PATCH] Greatly improved the 404 error message when DEBUG=True. If none of the urlpatterns matches, Django now displays a list of all the urlpatterns it tried. This should catch a lot of newbie errors, and it's helpful even for power users. git-svn-id: http://code.djangoproject.com/svn/django/trunk@414 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/handlers/base.py | 33 +++++++++++++++++++++++++++------ django/core/urlresolvers.py | 18 +++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 115b6ad56f..356e9c45a9 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -60,9 +60,9 @@ class BaseHandler: if response: return response return callback(request, **param_dict) - except exceptions.Http404: + except exceptions.Http404, e: if DEBUG: - return self.get_technical_error_response(is404=True) + return self.get_technical_error_response(is404=True, exception=e) else: callback, param_dict = resolver.resolve404() return callback(request, **param_dict) @@ -99,14 +99,35 @@ class BaseHandler: callback, param_dict = resolver.resolve500() return callback(request, **param_dict) - def get_technical_error_response(self, is404=False): + def get_technical_error_response(self, is404=False, exception=None): """ Returns an HttpResponse that displays a TECHNICAL error message for a fundamental database or coding error. """ - error_string = "There's been an error:\n\n%s" % self._get_traceback() - responseClass = is404 and httpwrappers.HttpResponseNotFound or httpwrappers.HttpResponseServerError - return responseClass(error_string, mimetype='text/plain') + if is404: + from django.conf.settings import ROOT_URLCONF + from django.utils.html import escape + html = ['<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'] + html.append('<html><head><title>Error 404</title>') + # Explicitly tell robots not to archive this, in case this slips + # onto a production site. + html.append('<meta name="robots" content="NONE,NOARCHIVE" />') + html.append('</head><body><h1>Error 404</h1>') + try: + tried = exception.args[0]['tried'] + except (IndexError, TypeError): + if exception.args: + html.append('<p>%s</p>' % escape(exception.args[0])) + else: + html.append('<p>Using the URLconf defined in <code>%s</code>, Django tried these URL patterns, in this order:</p>' % ROOT_URLCONF) + html.append('<ul>%s</ul>' % ''.join(['<li><code>%s</code></li>' % escape(t).replace(' ', ' ') for t in tried])) + html.append("<p>The current URL, <code><strong>%r</strong></code>, didn't match any of these.</p>" % exception.args[0]['path']) + html.append("<hr /><p>You're seeing this error because you have <code>DEBUG = True</code> in your Django settings file. Change that to <code>False</code>, and Django will display a standard 404 page.</p>") + html.append('</body></html>') + return httpwrappers.HttpResponseNotFound('\n'.join(html)) + else: + output = "There's been an error:\n\n%s" % self._get_traceback() + return httpwrappers.HttpResponseServerError(output, mimetype='text/plain') def _get_traceback(self): "Helper function to return the traceback as a string" diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index 50f9336171..178c2d139a 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -10,6 +10,9 @@ a string) and returns a tuple in this format: from django.core.exceptions import Http404, ViewDoesNotExist import re +class Resolver404(Http404): + pass + def get_mod_func(callback): # Converts 'django.views.news.stories.story_detail' to # ['django.views.news.stories', 'story_detail'] @@ -52,15 +55,20 @@ class RegexURLResolver: self.urlconf_name = urlconf_name def resolve(self, path): + tried = [] match = self.regex.search(path) if match: new_path = path[match.end():] for pattern in self.url_patterns: - sub_match = pattern.resolve(new_path) - if sub_match: - return sub_match - # None of the regexes matched, so raise a 404. - raise Http404, "Tried all URL patterns but didn't find a match for %r" % path + try: + sub_match = pattern.resolve(new_path) + except Resolver404, e: + tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0]['tried']]) + else: + if sub_match: + return sub_match + tried.append(pattern.regex.pattern) + raise Resolver404, {'tried': tried, 'path': new_path} def _get_urlconf_module(self): self.urlconf_module = __import__(self.urlconf_name, '', '', [''])