1. Grigory Fateyev

    26.05.2010

    0 ↑
    1 ↓
    Здравствуйте!

    Решил спросить здесь про Tornado, в надежде, что есть люди использующие этот инструмент в разработке. В официальной рассылке я поддержки не нашёл. :)

    И так, сама проблема. В проекте написанном на Tornado необходимо дописать авторизацию по ключу в cookie 'lp_login'. В классе BaseHandler, от которого наследуются все остальные handlers, есть функция в которой проверяется авторизован ли пользователь или нет:
        def get_current_user(self):
    user_id = self.get_secure_cookie("user")
    user_cookie = self.get_cookie("lp_login")
    if user_id:
    self.set_secure_cookie("user", user_id)
    return Author.objects.get(id=int(user_id))
    elif user_cookie:
    # Здесь работает код разбора user_cookie и установка cookie для аваторизации
    Но главная проблема в том, что в этой секции необходим запрос на внешний Auth API, где проверяется наличие пользователя в базе и активен ли он. Так как запрос на внешний урл требует время и при этом блокируются все запросы, это не приемлемо. По этому я решил писать асинхронный запрос. Вот полный код класса. Запрос отправляется асинхронно и данные возвращаются, но не устанавливаются cookie для последующей авторизации(строка 32) и есть вторая проблема. Так как запрос асинхронный, запрашиваемая страница рендерится раньше чем приходит асинхронный ответ и на этой странице секция входа открывается повторно, что и естественно, cookie то не установились ещё. Для конечного пользователя это будет проблема. Что можно предпринять в данном случае?

    Я уже пробовал сделать и с перенаправлением на собственный handler, куда вынесена вся логика, но и этот способ не подошёл.

    Понимаю, что вопрос слишком специфичен и вряд ли кто поможет конкретным советом, но возможно подскажите другое архитектурное решение. Буду благодарен за любые подсказки!
  2. evilkost

    26.05.2010

    0 ↑
    0 ↓

    Так как запрос асинхронный, запрашиваемая страница рендерится раньше чем приходит асинхронный ответ и на этой странице секция входа открывается повторно, что и естественно, cookie то не установились ещё.

    Асинхронный запросы != отдать юзеру ответ сразу. Ответ должен генерится в self.on_response и set_cookie туда должен попасть.

    Покажите полный код в котором обрабатывается запрос и генерится ответ пользователю.

  3. Так в документации написано, что надо обернуть функцию в декоратор asyncronous и явным образом завершить обработку запроса в хендлере.

  4. Grigory Fateyev

    27.05.2010

    0 ↑
    1 ↓
    evilkot, вот как это выглядит у меня: http://gist.github.com/415678 (некоторые вещи сократил, к делу они не относятся). Буду очень благодарен если подтолкнёте в нужном направлении.

    Александр, дока читана перечитана, просто упустил эту строку при copy'n'paste. :)
  5. дока читана перечитана, просто упустил эту строку при copy'n'paste. :)

    Какую строку?

    Если вы сделаете по документации дергание внешней ручки, то у вас не долно быть проблем.

    В приведенном в гисте коде я не увидел метод-обработчик хендлера, обернутый в декоратор. И не увидел явного вызова self.finish()

  6. Grigory Fateyev

    27.05.2010

    0 ↑
    1 ↓
    В приведенном в гисте коде я не увидел метод-обработчик хендлера, обернутый в декоратор.
    Александр, в классе BaseHandler строка 22 оборачивает метод get_current_user.
    И не увидел явного вызова self.finish()
    Если честно, я не совсем понимаю что и зачем я должен заканчивать в данном случае. :)
  7. evilkost

    27.05.2010

    0 ↑
    0 ↓
    Grigory Fateyev
    покажите код от метода self.get / self.post до того места где у вас self.write()

    (По показанному отрывку у меня создается ощущение, что вы принимая запрос от пользователя начинаете проверять авторизацию. Если её нет, вызываете проверку авторизации асиннхронно, и отдаете ответ пользователю, а когда проверка закончена set_cookie уже некуда писать, т.к. ответ пользователю ушел. )


    Про self.finish()
    >>> Non-blocking, asynchronous requests
    >>>
    >>> When a request handler is executed, the request is automatically finished. Since Tornado uses a non-blocking I/O style, you can override this default behavior if you want a request to remain open after the main request handler method returns using the tornado.web.asynchronous decorator.
    >>>
    >>> When you use this decorator, it is your responsibility to call self.finish() to finish the HTTP request, or the user's browser will simply hang

    Следовательно у вас над get/post не висит декоратора @tornado.web.asynchronous что подтверждает мою догадку.
  8. Grigory Fateyev

    27.05.2010

    0 ↑
    1 ↓
    покажите код от метода self.get / self.post до того места где у вас self.write()
    Вот класс, который соответствует урлу "/":
    class HomeHandler(BaseHandler):
    @tornado.web.asynchronous
    def get(self):
    entries = Event.objects.published()[:5]
    archives = Event.objects.published()[5:10]
    for e in entries:
    e.waiters = get_users_count(str(e.id))
    for e in archives:
    e.waiters = get_users_count(str(e.id))
    if len(entries) == 0:
    self.render("404.html")
    self.render("home.html", entries=entries, archives=archives, **self.context)
    Я думаю, что self.render() так-же завершает запрос просто отрендерив шаблон, может в этом и проблема?
    (По показанному отрывку у меня создается ощущение, что вы принимая запрос от пользователя начинаете проверять авторизацию. Если её нет, вызываете проверку авторизации асиннхронно, и отдаете ответ пользователю, а когда проверка закончена set_cookie уже некуда писать, т.к. ответ пользователю ушел. )
    Вы правильно поняли меня. А что же тогда делать?
    Про self.finish()
    ...
    То, что я должен закрывать запрос после каждого асинхронного соединения мне понятно, только как вы заметили выше, HomeHandler уже отдал шаблон и закрыл соединение, и что закрывать мне я не понимал. Теперь я понял что мой подход в корне не верен, а как сделать правильно я и не представляю.
  9. evilkost

    27.05.2010

    0 ↑
    0 ↓

    Я думаю, что self.render() так-же завершает запрос просто отрендерив шаблон, может в этом и проблема? Да внутри у него вызывается self.finish

    Теперь я понял что мой подход в корне не верен, а как сделать правильно я и не представляю. self.render должен вызываться в self.on_response как совместить с авторизацией с ходу не скажу, но не думаю что проблема будет...

    посмотрите тут http://github.com/facebook/tornado/blob/master/demos/auth/authdemo.py и другие примеры с использовангие асинхронных обработчиков

  10. Grigory Fateyev

    27.05.2010

    0 ↑
    1 ↓
    Я как раз смотрю примеры из demos и auth.py, в примерах используется self.authenticate_redirect() после асинхронного запроса, видимо в эту сторону и надо копать.
  11. Grigory Fateyev

    28.05.2010

    0 ↑
    1 ↓
    self.render должен вызываться в self.on_response как совместить с авторизацией с ходу не скажу, но не думаю что проблема будет..
    Если честно, я не могу представить как get функции handler'а передать необходимые переменные для работы self.render(template, **kwargs) в функции ответа on_response. Может какие мысли будут?
  12. evilkost

    28.05.2010

    0 ↑
    0 ↓

    передать необходимые переменные для работы self.render(template, **kwargs) в функции ответа on_response

    например в get пишем self.foo = 'bar', далее везде можем использовать self.foo

  13. Grigory Fateyev

    29.05.2010

    0 ↑
    1 ↓
    К сожалению, этот вариант не работает. get функция обёрнута в декоратор @tornado.web.asynchronous и соответственно ждёт завершение соединения. Страница просто весит в режиме ожидания...
  14. gasoid.ya.ru

    29.05.2010

    0 ↑
    0 ↓
    а нельзя сохранять где-то флаг получения авторизации. и следовательно не рендерить страницу ?
  15. Grigory Fateyev

    31.05.2010

    0 ↑
    0 ↓
    Пока решил проблему написав декоратор, который проверяет наличие данного ключа в cookie и в случае его обнаружения, редиректит на отдельный handler со всей логикой (вот там то я и делаю асинхронный запрос на внешний Auth API) и возвращаю пользователя обратно. Получилось страшненько, но работает!

    P.S. Спасибо ответившим за помощь и поддержку!
  16. Странно почему вы так и не осилили нормальный способ.

  17. Grigory Fateyev

    01.06.2010

    0 ↑
    0 ↓
    В том то и дело, Александр, что я хочу странного (с точки зрения Tornado), а как эту "странность" реализовать, не придумал. Но вопрос не заброшен, главное что сдвинулся с мёртвой точки.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.