Something I've struggled with for a long time I've finally solved, right or
wrong :-)

My application is about 95% ajax based, so most of my calls to the server
are XMLHttpRequest.  I keep stuff in the session, and a 2 minute heartbeat
keeps an active user list in my db.  All pages are controlled by role based
security, so users can only get the content they need.  This complexity
creates a lot of room for errors, and I need to communicate precisely with
the user, as well as pass detailed info/exception information back to the
client.

The default behavior in web.py seems to be to trap all python exceptions
behind the built in _InternalError, which will throw back an "internal
server error" to the client as an HTTP 500 status.  This isn't precise
enough for me to interact with the user as I'd like.

Also, I'm trying to be very "pythonic" in my code so I'm raising custom
exceptions for errors *as well as* informational cases.  I've got about 20
custom exceptions defined.

Finally, every request to the server goes through an app_processor, so I
can verify that the user is still valid and has an active session. (*my*
internal session mechanis, not the web.py session, although I use the
latter to implement mine.)

Here's what I did to make it all work.  (This is snippets of course, not
the whole file.)

class ExceptionHandlingApplication(web.application):
    """
    This is an overload of handle_with_processors from the standard
web.application class.
    """
    def handle_with_processors(self):
        def process(processors):
            try:
                if processors:
                    p, processors = processors[0], processors[1:]
                    return p(lambda: process(processors))
                else:
                    return self.handle()
            except (web.HTTPError, KeyboardInterrupt, SystemExit):
                raise
            except InfoException as ex:
                # we're using a custom HTTP status code to indicate
'information' back to the user.
                web.ctx.status = "280 Informational Response"
                logger.exception(ex.__str__())
                return ex.__str__()
            except SessionError as ex:
                # indicates the code determined this session is done.
                logger.exception(ex.__str__())
                # now, all our ajax calls are from jQuery, which sets a
header - X-Requested-With
                # so if we have that header, it's ajax, otherwise we can
redirect to the login page.
                # and the client handles the different HTTP status codes!

                # a session error means we kill the session
                session.kill()

                if web.ctx.env.get("HTTP_X_REQUESTED_WITH") ==
"XMLHttpRequest":
                    web.ctx.status = "480 Session Error"
                    return ex.__str__()
                else:
                    logger.debug("Standard Request - redirecting to the
login page...")
                    raise web.seeother('/static/login.html')
            except Exception as ex:
                # web.ctx.env.get('HTTP_X_REQUESTED_WITH')
                web.ctx.status = "400 Bad Request"
                logger.exception(ex.__str__())
                return ex.__str__()

        return process(self.processors)


And here's how I set up the application:

    app = ExceptionHandlingApplication(urls, globals(), autoreload=True)
    app.add_processor(auth_app_processor)

I hope this helps someone!
NSC

-- 
You received this message because you are subscribed to the Google Groups 
"web.py" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to webpy+unsubscr...@googlegroups.com.
To post to this group, send email to webpy@googlegroups.com.
Visit this group at http://groups.google.com/group/webpy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to