Комментарии 39
У меня при решении такой задачи всегда возникает вопрос целесообразности таких трудозатрат.
Самый простой способ, который приходит в голову, сделать авторизованному пользователю по таймауту (например 10-15 минут, зависит от настроек сессии на сервере) аякс-запрос на спец страницу, на которой продлевать сессию (с минимальными затратами), чтобы пользователь не разавторизовывался.
Самый простой способ, который приходит в голову, сделать авторизованному пользователю по таймауту (например 10-15 минут, зависит от настроек сессии на сервере) аякс-запрос на спец страницу, на которой продлевать сессию (с минимальными затратами), чтобы пользователь не разавторизовывался.
Полностью согласен тут. Более того, я думаю, что вообще не стоит разаутентифицировать по тайм-ауту. Вообще я сделал именно так, а не иначе, потому-что: править бекенд — не хотелось, там очень страшно и можно навлечь на себя плохих духов; делать вашим способом — просто не интересно :), ну и мой несколько надежней в итоге, т.к. лучше не опираться что-то, выполняющееся по тайм-ауту (это мое имхо, но, думаю, не только мое)
Это приходит с годами. А когда даже в терминологии плохо разбираешься, приходится делать, как автор статьи ).
Лично я бы тоже так делал. Мало того, навесил бы на эту функцию еще и какое-то полезное действие, например узнать, как там статусы каких-то задач поживают. И все это работало бы при помощи стандартной библиотеки.
Ребят, давайте обсуждать решение тут, а не проблему, которую я с помощью него решил
А что обсуждать?
Создать очередь для запросов, если не хочется что-то терять. Удалять из очереди обработанные запросы (статус 200), вызывать колбэк-функцию при статусе 401.
Можно реализовать на стандартных библиотеках, навесив обработчики на события ajaxError, ajaxComplete
Создать очередь для запросов, если не хочется что-то терять. Удалять из очереди обработанные запросы (статус 200), вызывать колбэк-функцию при статусе 401.
Можно реализовать на стандартных библиотеках, навесив обработчики на события ajaxError, ajaxComplete
Если разавторизация по таймауту — обязательна, то ваше решение просто сведет это требование на нет. Так человек при простое разлогинивается, а в вашем случае будет висеть открытое окошко бесконечно долго. А предложенное решение и проблему с повторным входом и сохраняет состояние.
При такой постановке задачи не могу с Вами не согласится. Вы совершенно правы!
Но автор упоминает статусы ответа сервера 401 и 403, это работает для HTTP-аутентификации. На большинстве же сайтов будет ответ 200, и авторизацию пользователя нужно проверять дополнительно.
Я обычно на стороне бек-энда проверяю авторизацию и при необходимости отдаю статус о необходимости авторизации, так как на статусе ответа не мог решить вопрос.
Но автор упоминает статусы ответа сервера 401 и 403, это работает для HTTP-аутентификации. На большинстве же сайтов будет ответ 200, и авторизацию пользователя нужно проверять дополнительно.
Я обычно на стороне бек-энда проверяю авторизацию и при необходимости отдаю статус о необходимости авторизации, так как на статусе ответа не мог решить вопрос.
А при чём тут вообще авторизация? Речь идёт об аутентификации. 401 тут вполне уместен, а вот 403 нет.
Да, текущее локальное состояние.
А теперь немножко дегтя.
Вы открыли страницу, произвели некоторые действия, например, нажали удаление какого-то элемента. Но, пропал WiFi, действие не выполнилось. Вы закрыли ноут, он ушел в Hibernate-состояние. На работе вы открыли эту страницу и повторно выполнили действие. Потом включили ноут, залогинились, и… скрипт автоматически должен выполнить какие-то отложенные действия, потому что так задумано. Хорошо, если они не имеют никакого негативного влияния, и спокойно могут отработаться на стороне сервера, но вы можете попасть в нелицеприятную ситуацию, когда, например, кому-то отправится письмо, в котором написано немного не то, что вы написали в адекватном состоянии позже.
Таким образом, решая одну проблему, мы сталкиваемся с другой — после логина нужно от сервера получать некий хэш состояния, который сравнивать со своим, чтобы убедиться, что за это время на сервере ничего не изменилось. В случае несовпадения состояний, нужно реинициализировать окружение.
А теперь немножко дегтя.
Вы открыли страницу, произвели некоторые действия, например, нажали удаление какого-то элемента. Но, пропал WiFi, действие не выполнилось. Вы закрыли ноут, он ушел в Hibernate-состояние. На работе вы открыли эту страницу и повторно выполнили действие. Потом включили ноут, залогинились, и… скрипт автоматически должен выполнить какие-то отложенные действия, потому что так задумано. Хорошо, если они не имеют никакого негативного влияния, и спокойно могут отработаться на стороне сервера, но вы можете попасть в нелицеприятную ситуацию, когда, например, кому-то отправится письмо, в котором написано немного не то, что вы написали в адекватном состоянии позже.
Таким образом, решая одну проблему, мы сталкиваемся с другой — после логина нужно от сервера получать некий хэш состояния, который сравнивать со своим, чтобы убедиться, что за это время на сервере ничего не изменилось. В случае несовпадения состояний, нужно реинициализировать окружение.
Этим занимается конечный код, который делает сами запросы и меняет содержимое страницы в зависимости от ответа. Или я что-то не так понял?
Сервер должен проверять не удален ли уже элемент, не проголосовал ли заново пользователь и тому подобное. Не было ли ранее отправлено точно такое же письмо и так далее.
Не вижу никакой проблемы.
Не вижу никакой проблемы.
Не всегда нужно продлевать. Иногда нужно наоборот — блокировать пользователя через пять минут неиспользования и требовать пароль.
Автор всё правильно сделал — пользователь надёжно блокируется сервером, состояние приложения не теряется. У нас ещё polling раз в минуту ходит на сервер и проверяет, не заблокирован ли пользователь.
Автор всё правильно сделал — пользователь надёжно блокируется сервером, состояние приложения не теряется. У нас ещё polling раз в минуту ходит на сервер и проверяет, не заблокирован ли пользователь.
+1 поддержка сессии должна быть реализована на бекэнде, то есть если идут запросы, то значит сессия еще жива и нет смысла ее обрывать, а вот если последний запрос давно не поступал… то тут уже от задач проекта зависит.
Если у вас была проблема, и вы придумали решить ее с помощью regexp'а ajax'а, то теперь у вас две проблемы.
(с) народная программистская мудрость
(с) народная программистская мудрость
Если ajax для вас — проблема, вы — некомпетентны, и в it вам делать нечего.
(с) очевидная капитанская мудрость
(с) очевидная капитанская мудрость
Да, мне явно делать нечего в IT и я очень некомпетентен.
И это не смотря на то, что уже как 5 лет работают SaaS-сервисы для корпоративного сектора, построенные мной полностью на AJAH, AJAX (был, пришлось отказаться), и даже AJ. Но кому это интересно?
Я понятия не имею про проблемы рассинхронизации состояний, про очереди выполнения запросов, про отложенные вызовы и прочую лабудень, которая ну ни как не может быть проблемой.
Так что продолжайте ставить минусы, я явно это заслужил. Посмел свое невежество на людях показать…
И это не смотря на то, что уже как 5 лет работают SaaS-сервисы для корпоративного сектора, построенные мной полностью на AJAH, AJAX (был, пришлось отказаться), и даже AJ. Но кому это интересно?
Я понятия не имею про проблемы рассинхронизации состояний, про очереди выполнения запросов, про отложенные вызовы и прочую лабудень, которая ну ни как не может быть проблемой.
Так что продолжайте ставить минусы, я явно это заслужил. Посмел свое невежество на людях показать…
api.jquery.com/ajaxComplete/
или
api.jquery.com/jQuery.ajaxSetup/ (если нужно иметь возможность перегрузить на местах)
$(document).ajaxComplete(function(e,xhr){
if (xhr.status == "403") {
location.href="/error/deny";
}
});
или
api.jquery.com/jQuery.ajaxSetup/ (если нужно иметь возможность перегрузить на местах)
$.ajaxSetup({
complete: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.status == "403") {
location.href="/error/deny";
}
}
});
s0rr0w уже предлагал: habrahabr.ru/post/148140/#comment_4999845
Ох, ну говорю же, не должны выполнятся навешанные конечным кодом, если нужна переаутентификация
*конечным кодом ивенты
Если честно, то это проблема нетранзакционной логики построения общения клиента и сервера.
Я не говорю, что это плохо, что реализация неудачная. Нет, все хорошо, код работал, опыт накапливался. Но это больше «синхронное» мышление, потому что вы на вызов хотите сразу получить ответ. Чтобы от этого избавиться, вам все равно придется рефакторить код и переходить к транзакционному мышлению
Что есть транзакция применительно к данному случаю? Давайте рассмотрим логику работы некого модуля транзакций
1. Сформировать пакет данных для запроса и записать как транзакцию в очереди
2. Вызвать обработчик очереди.
3. Если транзакция прошла, то выполняем правильный колбэк и удаляем ее из очереди
4. Если не прошла с кодом 401, то выполняем колбэк аутентификации.
5. Если не прошла с кодом 409 (conflict), то нужно просто отменить транзакцию
Основное отличие в том, что вы никогда не вызываете напрямую send-методы, а используете только модуль транзакций. Хранение данных в очереди позволит вам решить еще одну проблему — флуд запросов. Например, пользователи часто любят два раза нажимать на кнопку, и тогда на сервер уходят два запроса.
Я не говорю, что это плохо, что реализация неудачная. Нет, все хорошо, код работал, опыт накапливался. Но это больше «синхронное» мышление, потому что вы на вызов хотите сразу получить ответ. Чтобы от этого избавиться, вам все равно придется рефакторить код и переходить к транзакционному мышлению
Что есть транзакция применительно к данному случаю? Давайте рассмотрим логику работы некого модуля транзакций
1. Сформировать пакет данных для запроса и записать как транзакцию в очереди
2. Вызвать обработчик очереди.
3. Если транзакция прошла, то выполняем правильный колбэк и удаляем ее из очереди
4. Если не прошла с кодом 401, то выполняем колбэк аутентификации.
5. Если не прошла с кодом 409 (conflict), то нужно просто отменить транзакцию
Основное отличие в том, что вы никогда не вызываете напрямую send-методы, а используете только модуль транзакций. Хранение данных в очереди позволит вам решить еще одну проблему — флуд запросов. Например, пользователи часто любят два раза нажимать на кнопку, и тогда на сервер уходят два запроса.
Это интересно, я никогда ничего подобного не слышал в контексте джаваскрипта (но мне много аналогий приходит связанных с Doctrine2, UnitOfWork особенно). А есть какие-то готовые решения, которые так работают?
Единственное, что это немного перегруженная логика, точнее не логика, логика то как раз довольно прозрачная, но реализация, я чувствую, жесть будет.
Единственное, что это немного перегруженная логика, точнее не логика, логика то как раз довольно прозрачная, но реализация, я чувствую, жесть будет.
Скорее всего есть. Вопрос только — где :)
В коде особо менять ничего не нужно, запросы как посылались путем
Вы зря пугаетесь, нужно просто правильно все в голове уложить, реализация там копеечная.
В коде особо менять ничего не нужно, запросы как посылались путем
TransactionManager.send( dataObj, okHandler, errHandler )
, так и будут посылаться. Что изменяется — так это способ обработки ответа. Вы зря пугаетесь, нужно просто правильно все в голове уложить, реализация там копеечная.
И еще проблема — юзер хочет все сразу, по веянию его курсора. А вы, я так понимаю, предлагаете собирать бэтчи, и по количеству, либо таймауту, коммитить их?
Все будет и дальше работать по веянию курсора.
Давайте поясню немного детальнее.
После того, как была вызвана функция send(), вы должны отскладировать ее параметры где-то во внутреннем массиве запросов
Дальше выполняется не функция отсылки запроса, а функция проверки общего состояния системы. Что она должна делать? Пытаемся выполнить первую транзакцию из списка
Если у нас 401, то выполняем обработчик аутентификации, который вставит запрос аутентификации перед всей очередью и снова же выполнит функцию отсылки запроса.
Если первая транзакция выполнена со статусом 200, то выполняем вторую, третью и так далее.
Если в массиве транзакций уже есть похожая, то мы туда ее не добавляем. Ничего сложного
Давайте поясню немного детальнее.
После того, как была вызвана функция send(), вы должны отскладировать ее параметры где-то во внутреннем массиве запросов
transactionList = [ transaction1 ]
. Дальше выполняется не функция отсылки запроса, а функция проверки общего состояния системы. Что она должна делать? Пытаемся выполнить первую транзакцию из списка
transactionList[0]
. Если в ответ прислали 409, то очищаем массив транзакций transactionList = []
и выполняем обработчик перезагрузки состояний (скорее всего интерфейса целиком). Если у нас 401, то выполняем обработчик аутентификации, который вставит запрос аутентификации перед всей очередью и снова же выполнит функцию отсылки запроса.
[ authTransaction, transaction1 ]
. Если первая транзакция выполнена со статусом 200, то выполняем вторую, третью и так далее.
Если в массиве транзакций уже есть похожая, то мы туда ее не добавляем. Ничего сложного
Ну вообще-то нефиг юзать XMLHttpRequest напрямую. Если его создание у вас было бы в какой-то фабрике (или вы пользовались бы какими-то обёртками вокруг него), то вопрос и яйца выеденного не стоит — подменили реализацию и всё.
Какой-то кошмарный костыль.
1. При активном использовании ajax, даже jquery-метод можно обернуть в какой-то свой, чтобы абсолютно все запросы проходили через него — но это делается изначально.
2. Если уже есть код с кучей ajax-вызовов в разных местах — проще и правильнее убрать всю
эту копипасту и отрефакторить к варианту 1.
3. Если лень рефакторить — можно подменить\расширить метод jQuery.ajax, либо создать новый jQuery.myAjax и автозаменой пройтись по всему проекту.
Патчить дефолтные «классы»\«объекты» как-то всё нутро противиться :)
1. При активном использовании ajax, даже jquery-метод можно обернуть в какой-то свой, чтобы абсолютно все запросы проходили через него — но это делается изначально.
2. Если уже есть код с кучей ajax-вызовов в разных местах — проще и правильнее убрать всю
эту копипасту и отрефакторить к варианту 1.
3. Если лень рефакторить — можно подменить\расширить метод jQuery.ajax, либо создать новый jQuery.myAjax и автозаменой пройтись по всему проекту.
Патчить дефолтные «классы»\«объекты» как-то всё нутро противиться :)
Если и идти таким путём, то улучшить текущий вариант можно в направлении большей независимости:
* отделить реализацию wrapper`а от внутренних событий, создав для них события
* все интерфейсные и прочие реаутентификационные штуки уже в событиях, вытащенных наружу прописать
В итоге это решение превратится в компонент — щас это хардкод конкретной задачи, который на другой проект никак не переходит без переписи внутреннего мяса.
PS: И конечно тестирование :) Уже в опере там какие-то проблемы. Именно поэтому товарищи выше не рекомендуют корные компоненты изобретать. Не потому, что это невозможно, а потому, что затраты на тестирование будут гораздо выше, чем на разработку.
* отделить реализацию wrapper`а от внутренних событий, создав для них события
* все интерфейсные и прочие реаутентификационные штуки уже в событиях, вытащенных наружу прописать
В итоге это решение превратится в компонент — щас это хардкод конкретной задачи, который на другой проект никак не переходит без переписи внутреннего мяса.
PS: И конечно тестирование :) Уже в опере там какие-то проблемы. Именно поэтому товарищи выше не рекомендуют корные компоненты изобретать. Не потому, что это невозможно, а потому, что затраты на тестирование будут гораздо выше, чем на разработку.
А вы везде использовали XMLHttpRequest без обёртки? :)
Мягко говоря, это странно, если ваш проект не ограничивается двумя-тремя ajax запросами.
Мягко говоря, это странно, если ваш проект не ограничивается двумя-тремя ajax запросами.
Прошу прощения за видимый само-пиар, полная обертка: github.com/ilinsky/xmlhttprequest с фиксацией большинства багов старых и новых браузеров: www.ilinsky.com/articles/XMLHttpRequest/ (memory leaks, status change ordering etc.) Используется в многих проектах с 2007 года.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Подмена XMLHttpRequest или как не трогая тонны готового js-кода изменить поведение всех ajax-запросов