Opened 7 years ago

Closed 4 years ago

Last modified 20 months ago

#12250 closed Bug (wontfix)

process_view middleware prevents process_exception handling

Reported by: Michael Manfre Owned by: Michael Manfre
Component: Core (Other) Version: master
Severity: Normal Keywords: middleware
Cc: Walter Doekes Triage Stage: Design decision needed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

If process_view() calls a view that raises and exception, it is handled by django.core.handlers.base.BaseHandler.handle_uncaught_exception() instead of being passed to the middleware for process_exception() handling.

Attachments (3)

django-base-handlers-view-middleware.patch (1.2 KB) - added by Michael Manfre 7 years ago.
django-base-handlers-view-middleware-with-tests.diff (7.0 KB) - added by Michael Manfre 6 years ago.
Patch against trunk rev 13959
12250.1.diff (5.8 KB) - added by Ramiro Morales 6 years ago.
Patch modified and simplfied

Download all attachments as: .zip

Change History (21)

Changed 7 years ago by Michael Manfre

comment:1 Changed 7 years ago by Jeremy Dunck

Needs tests: set
Owner: changed from nobody to Jeremy Dunck
Patch needs improvement: set
Status: newassigned
Triage Stage: UnreviewedAccepted

comment:2 Changed 7 years ago by Jeremy Dunck

Keywords: middleware added
Owner: changed from Jeremy Dunck to nobody
Status: assignednew

comment:3 Changed 7 years ago by Jeremy Dunck

This really needs tests. It's a critical piece of code and there are lots of exception paths around there.

comment:4 Changed 7 years ago by anonymous

Needs tests: unset
Owner: changed from nobody to anonymous
Patch needs improvement: unset
Status: newassigned

Changed 6 years ago by Michael Manfre

Patch against trunk rev 13959

comment:5 Changed 6 years ago by Michael Manfre

milestone: 1.3
Owner: changed from anonymous to Michael Manfre
Status: assignednew

Updated the patch against trunk and hoping to get this in to 1.3

comment:6 Changed 6 years ago by Ramiro Morales

Triage Stage: AcceptedDesign decision needed

So the ticket originally was about exceptions generated in the view itself not being caught an processed by .process_exception().

But the code modification and accompanying tests proposed aren't about exceptions in the view but exceptions originated in a middleware process_view() method.

The first is no longer an issue (I suspect it was fixed in r14393 and r14938) and the second one still happens but I'm not sure the change of behavior in that case should happen. I'm adding a modified and simplified patch demonstrating both scenarios.

Because of that, I'm moving this to design decision needed.

Version 0, edited 6 years ago by Ramiro Morales (next)

Changed 6 years ago by Ramiro Morales

Attachment: 12250.1.diff added

Patch modified and simplfied

comment:7 Changed 6 years ago by Matt McClanahan

milestone: 1.3
Severity: Normal
Type: Bug

comment:8 Changed 5 years ago by Aymeric Augustin

UI/UX: unset

Change UI/UX from NULL to False.

comment:9 Changed 5 years ago by Aymeric Augustin

Easy pickings: unset

Change Easy pickings from NULL to False.

comment:10 Changed 4 years ago by kevin1024

I think the latest patch is missing the actual behavior modification. It seems to only have tests in it.

comment:11 Changed 4 years ago by Walter Doekes

Cc: Walter Doekes added
126	+        """
127	+        A <Middleware>.process_view() that generates an exception and another
128	+        middleware whose process_exception() provides a response. Result should
129	+        be that response.
130	+        """
131	+        settings.MIDDLEWARE_CLASSES = (
132	+            'regressiontests.base_handler.middleware.ProcessViewRaise',
133	+            'regressiontests.base_handler.middleware.ProcessExceptionResponse',
134	+        )

Well, I have a common scenario that would benefit from having the exception handler handle the process_view() exception:

django/middleware/csrf.py

calls

    request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
...
    self._load_post_and_files()
...
    return self._stream.read(*args, **kwargs)

IOError: error reading wsgi.input data

I'd love to ignore that particular IOError, and I can, when it's generated in the view. But I can't when it's generated in the CSRF process_view.

As for how: I'd expect it to be handled just as if it was an exception in the view itself. I.e. surround the _view_middleware calls with a try/except just like the "response = callback(...)" bit.

Regards,
Walter Doekes

comment:12 Changed 4 years ago by Anssi Kääriäinen

Another similar issue exists in process_request(). Consider case:

MIDDLEWARE_CLASSES = [
    'TransactionMiddleware',
    'SavesToDB',
    'RaisesException'
]

What happens is that TransactionMiddleware will open a transaction in process_request, savestodb will save to db and then RaisesException will raise an exception. The process_exceptions aren't called in this case. Finally process_response is called for every of the middlewares and thus TranscationMiddleware will commit the transaction. This is not good.

I see two solutions here - either we do the process_exception() dance for all middlewares for which process_request has been called (in process_view() case this means every middleware). Or, we decide that process_* should not ever raise an exception, and we go to panic mode if this happens. The panic mode would mean that we would try to release all resources (close connections etc) and then return a barebone 500 response.

To me the panic mode doesn't seem like a good idea.

I reported this in #19648. I marked that ticket as a duplicate of this one.

comment:13 Changed 4 years ago by Aymeric Augustin

This is almost the same problem as #16367 which I closed as wontfix.

Given the amount of discussion above, I'm not going to close as wontfix right now, but that's still my inclination.

comment:14 Changed 4 years ago by Aymeric Augustin

#12909 was closed as a duplicate.

comment:15 Changed 4 years ago by Aymeric Augustin

Resolution: wontfix
Status: newclosed

Overall, the current design of the request handler assumes that middleware methods won't raise exceptions. They must deal with their own exceptions. Exceptions raised by middleware will end up in handle_uncaught_exception and produce a 500.

This may have some drawbacks, but at least it's consistent. Aborting on the first exception also avoids the problem of deciding which middleware should or shouldn't run after the first exception.

I could consider a different behavior, but that would require a global proposal for consistent exception handling in middleware. It seems quite tricky to design something that is useful, consistent, and simple.

I'm going to close this ticket because it's a piecewise approach of the problem that introduces some inconsistency.

comment:16 Changed 4 years ago by Walter Doekes

Should I create a new ticket for the unhandled exception in the csrf middleware then? :)

comment:17 Changed 4 years ago by Aymeric Augustin

Yes please. Sorry, I missed that in the comments.

comment:18 Changed 20 months ago by Christopher Martin

"the current design of the request handler assumes that middleware methods won't raise exceptions. They must deal with their own exceptions. Exceptions raised by middleware will end up in handle_uncaught_exception and produce a 500."

That would be a great piece of information to include in https://docs.djangoproject.com/en/dev/topics/http/middleware/#writing-your-own-middleware

Note: See TracTickets for help on using tickets.
Back to Top