пятница, 29 января 2010 г.

Google App Engine. Обработка запросов с ошибками в webapp фреймворке

Идея переработать блог летала вокруг уже давно, назрело так сказать. Разделив работу на этапы, для начала решил перенести все ресурсы на CDN. Но как всегда захотелось халявы :).

Вспомнил о своем аккаунте на Google App Engine, промелькнула мысль "Можно же построить свой CDN, практически без затрат денег и управлять им так, как душа пожелает". Реализации, которые есть на текущий момент меня немного не устроили и работа закипела.

В качестве основы был выбран webapp фреймворк. Но речь как раз не о моей реализации, а о тонкостях самого фреймворка. Краткий пост хотел посвятить небольшому хаку по обработке запросов, в которых произошла ошибка.

В документации данная тема раскрыта в статье "Перенаправления, заголовки и коды статуса". И приведен следующий пример.

class MyHandler(webapp.RequestHandler):
def get(self):
self.response.out.write("Вы попросили что-то сделать.")
try:
doSomething()
self.response.out.write("Это сделано!")

except Error:
# Очистить вывод и вернуть сообщение об ошибке.
self.error(500)
Вроде бы все понятно, если вызвать self.error(500), то все данные которые записаны в ответ будут удалены и будет возвращено сообщение об ошибке.

Но при выполнении сразу можно заметить один маленький нюанс, сервер нам вернет пустую страницу в случае, когда отработает команда self.error(500) или self.error(404), или будет вызван данный метод с любой другой ошибкой. Но как и написано в документации, ответ будет имеет код ошибки 500 или 404, или любой другой, соответственно.

Поведение метода можно посмотреть в исходниках.
google.appengine.ext.webapp.RequestHandler

class Request(webob.Request):
...
def error(self, code):
"""Clears the response output stream and sets the given HTTP error code.

Args:
code: the HTTP status error code (e.g., 501)
"""
self.response.set_status(code)
self.response.clear()
...
Он всего лишь устанавливает нужный статус и очищает ответ. А хотелось же получить страницу с сообщением об ошибке :(!!!

Просто редиректить на страницу ошибки как-то не интересно. Добавлять после каждого вызова "self.error" рендер страницы ошибки тоже как-то не кошерно. Решил добавить небольшой хак

import os
import logging
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import RequestHandler

template_path = os.path.join(os.path.dirname(__file__), "templates")

def get_template_path(file_name):
return os.path.join(template_path, file_name)

def error_decorator(func):
def wrapper(*args, **kwarg):
handler = args[0]
status_code = args[1]
result = func(*args, **kwarg)
try:
html = {
404 : "404.html",
500 : "500.html"
}[status_code]
if not html:
return;
handler.response.out.write(template.render(get_template_path(html), template_values))
except Exception, e:
logging.error('Raise error in error_decorator. %s' % e)
return result
return wrapper

def main():
RequestHandler.error = error_decorator(RequestHandler.error)
application = webapp.WSGIApplication(
[('/', MainHandler),
], debug=True)
util.run_wsgi_app(application)

if __name__ == '__main__':
main()

При вызове метода "self.error(404)" кроме установки статуса и очистки данных response в ответ будет добавлена страница с сообщением об ошибке. Такой подход имеет ряд преимуществ.

Пользуйтесь! :)

0 коммент.:

Отправить комментарий