Ticket #6094: http_refactor_r7320.patch
File http_refactor_r7320.patch, 23.8 KB (added by , 17 years ago) |
---|
-
django/test/client.py
23 23 Uses the WSGI interface to compose requests, but returns 24 24 the raw HttpResponse object 25 25 """ 26 request_class = WSGIRequest 27 26 28 def __call__(self, environ): 27 29 from django.conf import settings 28 from django.core import signals29 30 30 31 # Set up middleware if needed. We couldn't do this earlier, because 31 32 # settings weren't available. 32 33 if self._request_middleware is None: 33 34 self.load_middleware() 34 35 35 dispatcher.send(signal=signals.request_started) 36 try: 37 request = WSGIRequest(environ) 38 response = self.get_response(request) 36 return self.handle_request(environ) 39 37 40 # Apply response middleware41 for middleware_method in self._response_middleware:42 response = middleware_method(request, response)43 response = self.apply_response_fixes(request, response)44 finally:45 dispatcher.send(signal=signals.request_finished)46 47 return response48 49 38 def store_rendered_templates(store, signal, sender, template, context): 50 39 "A utility function for storing templates and contexts that are rendered" 51 40 store.setdefault('template',[]).append(template) … … 94 83 ]) 95 84 return '\r\n'.join(lines) 96 85 97 class Client :86 class Client(object): 98 87 """ 99 88 A class that can act as a client for testing purposes. 100 89 … … 117 106 self.defaults = defaults 118 107 self.cookies = SimpleCookie() 119 108 self.exc_info = None 109 self.reraise_exceptions = True 120 110 121 111 def store_exc_info(self, *args, **kwargs): 122 112 """ … … 178 168 raise 179 169 180 170 # Look for a signalled exception and reraise it 181 if self.exc_info :171 if self.exc_info and self.reraise_exceptions: 182 172 raise self.exc_info[1], None, self.exc_info[2] 183 173 184 174 # Save the client and request that stimulated the response -
django/core/handlers/wsgi.py
6 6 from StringIO import StringIO 7 7 8 8 from django import http 9 from django.core import signals10 9 from django.core.handlers.base import BaseHandler 11 from django.dispatch import dispatcher12 10 from django.utils import datastructures 13 11 from django.utils.encoding import force_unicode 14 12 … … 195 193 self.load_middleware() 196 194 self.initLock.release() 197 195 198 dispatcher.send(signal=signals.request_started) 199 try: 200 try: 201 request = self.request_class(environ) 202 except UnicodeDecodeError: 203 response = http.HttpResponseBadRequest() 204 else: 205 response = self.get_response(request) 196 response = self.handle_request(environ) 206 197 207 # Apply response middleware208 for middleware_method in self._response_middleware:209 response = middleware_method(request, response)210 response = self.apply_response_fixes(request, response)211 finally:212 dispatcher.send(signal=signals.request_finished)213 214 198 try: 215 199 status_text = STATUS_CODE_TEXT[response.status_code] 216 200 except KeyError: -
django/core/handlers/base.py
3 3 from django import http 4 4 from django.core import signals 5 5 from django.dispatch import dispatcher 6 from django.core.urlresolvers import RegexURLResolver 6 7 8 9 def resolver(request): 10 """ 11 Returns a RegexURLResolver for the request's urlconf. 12 13 If the request does not have a urlconf object, then the default of 14 settings.ROOT_URLCONF is used. 15 """ 16 from django.conf import settings 17 urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) 18 return RegexURLResolver(r'^/', urlconf) 19 20 7 21 class BaseHandler(object): 8 22 # Changes that are always applied to a response (in this order). 9 23 response_fixes = [http.fix_location_header, … … 28 42 try: 29 43 dot = middleware_path.rindex('.') 30 44 except ValueError: 31 raise exceptions.ImproperlyConfigured , '%s isn\'t a middleware module' % middleware_path45 raise exceptions.ImproperlyConfigured('%s isn\'t a middleware module' % middleware_path) 32 46 mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:] 33 47 try: 34 48 mod = __import__(mw_module, {}, {}, ['']) 35 49 except ImportError, e: 36 raise exceptions.ImproperlyConfigured , 'Error importing middleware %s: "%s"' % (mw_module, e)50 raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e)) 37 51 try: 38 52 mw_class = getattr(mod, mw_classname) 39 53 except AttributeError: 40 raise exceptions.ImproperlyConfigured , 'Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)54 raise exceptions.ImproperlyConfigured('Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)) 41 55 42 56 try: 43 57 mw_instance = mw_class() … … 53 67 if hasattr(mw_instance, 'process_exception'): 54 68 self._exception_middleware.insert(0, mw_instance.process_exception) 55 69 56 def get_response(self, request): 57 "Returns an HttpResponse object for the given HttpRequest" 58 from django.core import exceptions, urlresolvers 59 from django.core.mail import mail_admins 60 from django.conf import settings 70 def handle_request(self, request): 71 """ 72 Takes a request and returns a response. 61 73 62 # Apply request middleware 74 Processing occurs according to the following diagram: 75 76 Request Response 77 | | 78 +----------+ +----------+ 79 | Request | | Response |<--------+ 80 |Middleware|---Response?--->|Middleware| | 81 +----------+ +----------+ 404/500 Response 82 | | | 83 ---------Request Exception Handler-----------------+--------- 84 | | 85 +---------+ +<-------+ 86 | URLConf | | | 87 +---------+ | +----------+ 88 | | |Exception | 89 +----------+ | |Middleware| 90 | View | | +----------+ 91 |Middleware|-----Response?------>+ | 92 +----------+ | Exception 93 | | | 94 ----------View Exception Handler------------+---------------- 95 | | 96 +------+ | 97 | View |-----Response--------->+ 98 +------+ 99 """ 100 dispatcher.send(signal=signals.request_started) 101 try: 102 try: 103 request = self.request_class(request) 104 except UnicodeDecodeError: 105 response = http.HttpResponseBadRequest() 106 else: 107 # See if the request middleware returns a response. 108 response = self.get_response(self.apply_request_middleware, 109 request) 110 # If not, then call the view. 111 if not response: 112 response = self.get_response(self.run_view_processing, 113 request) 114 # All responses go through the response middleware. 115 response = self.get_response(self.apply_response_middleware, 116 request, response) 117 response = self.apply_response_fixes(request, response) 118 return response 119 finally: 120 dispatcher.send(signal=signals.request_finished) 121 122 def apply_request_middleware(self, request): 123 """ 124 Applies request middleware one at a time until a response is returned. 125 If none of request middleware returns a response, then return None. 126 """ 63 127 for middleware_method in self._request_middleware: 64 128 response = middleware_method(request) 65 129 if response: 66 130 return response 67 131 68 # Get urlconf from request object, if available. Otherwise use default. 69 urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) 132 def run_view_processing(self, request): 133 """ 134 Apply view middleware and call view. 135 """ 136 callback, callback_args, callback_kwargs = resolver(request).resolve(request.path) 70 137 71 resolver = urlresolvers.RegexURLResolver(r'^/', urlconf) 138 # Apply view middleware 139 for middleware_method in self._view_middleware: 140 response = middleware_method(request, callback, callback_args, callback_kwargs) 141 if response: 142 return response 143 72 144 try: 73 callback, callback_args, callback_kwargs = resolver.resolve(request.path) 74 75 # Apply view middleware 76 for middleware_method in self._view_middleware: 77 response = middleware_method(request, callback, callback_args, callback_kwargs) 145 response = callback(request, *callback_args, **callback_kwargs) 146 except Exception, e: 147 # If the view raised an exception, run it through exception 148 # middleware, and if the exception middleware returns a 149 # response, use that. Otherwise, reraise the exception. 150 for middleware_method in self._exception_middleware: 151 response = middleware_method(request, e) 78 152 if response: 79 153 return response 154 raise 80 155 156 # Complain if the view returned None (a common error). 157 if response is None: 81 158 try: 82 response = callback(request, *callback_args, **callback_kwargs) 83 except Exception, e: 84 # If the view raised an exception, run it through exception 85 # middleware, and if the exception middleware returns a 86 # response, use that. Otherwise, reraise the exception. 87 for middleware_method in self._exception_middleware: 88 response = middleware_method(request, e) 89 if response: 90 return response 91 raise 159 view_name = callback.func_name # If it's a function 160 except AttributeError: 161 view_name = callback.__class__.__name__ + '.__call__' # If it's a class 162 raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)) 92 163 93 # Complain if the view returned None (a common error). 164 return response 165 166 def apply_response_middleware(self, request, response): 167 """ 168 Applies all response middleware to the response. 169 """ 170 for middleware_method in self._response_middleware: 171 response = middleware_method(request, response) 172 return response 173 174 def get_response(self, method, request, response=None): 175 """ 176 Returns an HttpResponse by calling the passed method with the given 177 HttpRequest object. If an HttpResponse is passed, it will also be 178 passed in the call to method. Exceptions raised by calling method 179 will be handled like so: 180 181 * Http404 exception - If settings.DEBUG is True, the 182 technical_404_response debug view will be called. If settings.DEBUG 183 is False, the URL resolver's handler404 view will be called. 184 * PermissionDenied exception - return HttpResponseForbidden. 185 * SystemExit exception - ignored. 186 * All other exceptions - If settings.DEBUG is True, the 187 technical_500_response debug view will be called. If settings.DEBUG 188 is False, the URL resolver's handler500 view will be called. 189 """ 190 from django.core import exceptions 191 from django.core.mail import mail_admins 192 from django.conf import settings 193 194 try: 94 195 if response is None: 95 try: 96 view_name = callback.func_name # If it's a function 97 except AttributeError: 98 view_name = callback.__class__.__name__ + '.__call__' # If it's a class 99 raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name) 100 101 return response 196 return method(request) 197 else: 198 return method(request, response) 102 199 except http.Http404, e: 103 200 if settings.DEBUG: 104 201 from django.views import debug 105 202 return debug.technical_404_response(request, e) 106 203 else: 107 callback, param_dict = resolver .resolve404()204 callback, param_dict = resolver(request).resolve404() 108 205 return callback(request, **param_dict) 109 206 except exceptions.PermissionDenied: 110 207 return http.HttpResponseForbidden('<h1>Permission denied</h1>') … … 128 225 message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr) 129 226 mail_admins(subject, message, fail_silently=True) 130 227 # Return an HttpResponse that displays a friendly error message. 131 callback, param_dict = resolver .resolve500()228 callback, param_dict = resolver(request).resolve500() 132 229 return callback(request, **param_dict) 133 230 134 231 def _get_traceback(self, exc_info=None): … … 145 242 for func in self.response_fixes: 146 243 response = func(request, response) 147 244 return response 148 -
django/core/handlers/modpython.py
2 2 from pprint import pformat 3 3 4 4 from django import http 5 from django.core import signals6 5 from django.core.handlers.base import BaseHandler 7 from django.dispatch import dispatcher8 6 from django.utils import datastructures 9 7 from django.utils.encoding import force_unicode, smart_str 10 8 … … 152 150 if self._request_middleware is None: 153 151 self.load_middleware() 154 152 155 dispatcher.send(signal=signals.request_started) 156 try: 157 try: 158 request = self.request_class(req) 159 except UnicodeDecodeError: 160 response = http.HttpResponseBadRequest() 161 else: 162 response = self.get_response(request) 153 response = self.handle_request(req) 163 154 164 # Apply response middleware165 for middleware_method in self._response_middleware:166 response = middleware_method(request, response)167 response = self.apply_response_fixes(request, response)168 finally:169 dispatcher.send(signal=signals.request_finished)170 171 155 # Convert our custom HttpResponse object back into the mod_python req. 172 156 req.content_type = response['Content-Type'] 173 157 for key, value in response.items(): -
tests/regressiontests/handler_exceptions/views.py
1 from django.http import HttpResponse 2 3 def good_view(request): 4 return HttpResponse("good view") 5 6 def bad_view(request): 7 raise Exception("View raised exception") -
tests/regressiontests/handler_exceptions/tests.py
1 from django.test import TestCase 2 from django.test.client import Client, ClientHandler 3 4 class GoodMiddleware(object): 5 def process_request(self, *args, **kwargs): 6 pass 7 def process_view(self, *args, **kwargs): 8 pass 9 def process_exception(self, *args, **kwargs): 10 pass 11 def process_response(self, request, response): 12 return response 13 14 15 class MiddlewareException(Exception): pass 16 class RequestMiddlewareException(MiddlewareException): pass 17 class ViewMiddlewareException(MiddlewareException): pass 18 class ExceptionMiddlewareException(MiddlewareException): pass 19 class ResponseMiddlewareException(MiddlewareException): pass 20 21 22 class BadRequestMiddleware(object): 23 def process_request(self, *args, **kwargs): 24 raise RequestMiddlewareException 25 26 class BadViewMiddleware(object): 27 def process_view(self, *args, **kwargs): 28 raise ViewMiddlewareException 29 30 class BadExceptionMiddleware(object): 31 def process_exception(self, *args, **kwargs): 32 raise ExceptionMiddlewareException 33 34 class BadResponseMiddleware(object): 35 def process_response(self, *args, **kwargs): 36 raise ResponseMiddlewareException 37 38 39 class MiddlewareResettingClientHandler(ClientHandler): 40 """ 41 A handler that clears loaded middleware after each request. 42 """ 43 def __call__(self, *args, **kwargs): 44 response = super(MiddlewareResettingClientHandler, self).__call__(*args, **kwargs) 45 # ClientHandler's __call__ method will load middleware if 46 # self._request_middleware is None. We set it to None here so that 47 # middleware will be reloaded next request. 48 self._request_middleware = None 49 return response 50 51 52 class HandlerClient(Client): 53 """ 54 A custom Client that doesn't reraise request-handling exceptions and uses 55 the MiddlewareResettingClientHandler. 56 """ 57 def __init__(self, *args, **kwargs): 58 super(HandlerClient, self).__init__(*args, **kwargs) 59 self.reraise_exceptions = False 60 self.handler = MiddlewareResettingClientHandler() 61 62 63 class HandlerExceptionTests(TestCase): 64 def setUp(self): 65 self.client = HandlerClient() 66 self.default_middleware = None 67 # Combinations that will raise errors. 68 self.bad_combinations = ( 69 # Middleware, request path 70 ('BadRequestMiddleware', '/good_view/', RequestMiddlewareException), 71 ('BadViewMiddleware', '/good_view/', ViewMiddlewareException), 72 ('BadResponseMiddleware', '/good_view/', ResponseMiddlewareException), 73 ('BadRequestMiddleware', '/bad_view/', RequestMiddlewareException), 74 ('BadViewMiddleware', '/bad_view/', ViewMiddlewareException), 75 ('BadExceptionMiddleware', '/bad_view/', ExceptionMiddlewareException), 76 ('BadResponseMiddleware', '/bad_view/', ResponseMiddlewareException), 77 ) 78 # Combinations that will not raise errors. 79 self.good_combinations = ( 80 ('GoodMiddleware', '/good_view/'), 81 # Exception middleware doesn't get run if the view doesn't raise 82 # an exception. 83 ('BadExceptionMiddleware', '/good_view/'), 84 ) 85 86 def setup_middleware(self, middleware): 87 """ 88 Append middleware to list of installed middleware. 89 """ 90 from django.conf import settings 91 # If we haven't saved the default middleware, do so now. 92 if self.default_middleware is None: 93 self.default_middleware = settings.MIDDLEWARE_CLASSES 94 middleware_path = 'regressiontests.handler_exceptions.tests.' + middleware 95 settings.MIDDLEWARE_CLASSES += (middleware_path,) 96 97 def reset_middleware(self): 98 """ 99 Restores middleware to the default installed middleware. 100 """ 101 from django.conf import settings 102 settings.MIDDLEWARE_CLASSES = self.default_middleware 103 104 def test_exception_handling(self): 105 """ 106 Tests that exceptions raised in middleware and views are properly 107 caught and render error templates. 108 """ 109 # Just for good measure, check that the good combination doesn't raise 110 # an error. 111 for middleware, path in self.good_combinations: 112 self.setup_middleware(middleware) 113 response = self.client.get('/handler_exceptions' + path) 114 self.assertContains(response, 'good view') 115 self.reset_middleware() 116 # Now test that all the bad combinations render the 500 template. 117 for middleware, path, exception in self.bad_combinations: 118 self.setup_middleware(middleware) 119 response = self.client.get('/handler_exceptions' + path) 120 self.assertContains(response, '500 Error', status_code=500) 121 self.assertTemplateUsed(response, '500.html') 122 self.reset_middleware() 123 124 def test_exception_raising(self): 125 """ 126 Tests that the correct exceptions bubble up from middleware with errors. 127 """ 128 # Turn on client's reraising of exceptions for this test. 129 self.client.reraise_exceptions = True 130 for middleware, path, exception in self.bad_combinations: 131 self.setup_middleware(middleware) 132 fail = False 133 try: 134 self.client.get('/handler_exceptions' + path) 135 # If above didn't raise an exception, then test failed. 136 fail = True 137 except exception: 138 # We got the exception we were looking for, test passes. 139 pass 140 except Exception: 141 # We got an exception that we wore not expecting. 142 from sys import exc_info 143 unexpected_exception = exc_info()[0] 144 self.fail("Expected %s to be raised, but got %s" 145 % (exception, unexpected_exception)) 146 if fail: 147 self.fail("Expected %s to be raised. Middleware: %s, Path: %s" 148 % (exception, middleware, path)) 149 self.reset_middleware() -
tests/regressiontests/handler_exceptions/models.py
1 # models file for tests to run. -
tests/regressiontests/handler_exceptions/urls.py
1 from django.conf.urls.defaults import * 2 3 import views 4 5 urlpatterns = patterns('', 6 (r'^good_view/$', views.good_view), 7 (r'^bad_view/$', views.bad_view), 8 ) -
tests/urls.py
18 18 # test urlconf for middleware tests 19 19 (r'^middleware/', include('regressiontests.middleware.urls')), 20 20 21 # urlconf for handler_exceptions tests. 22 (r'^handler_exceptions/', include('regressiontests.handler_exceptions.urls')), 23 21 24 (r'^utils/', include('regressiontests.utils.urls')), 22 25 )