Рассмотрим это представление Django, которое получит список элементов, связанных с текущим пользователем:
@login_required
def list_items(request, page_number=0):
items = Paginator(request.user.items, 5).page(page_number).object_list
return HttpResponse(cjson.encode(items))
Очевидно, он хочет использовать декоратор login_required
, чтобы ограничить доступ к представлению для зарегистрированных пользователей.в пользователях.
Что делает login_required
, когда неаутентифицированный пользователь пытается получить доступ к представлению?Возвращает HttpResponseRedirect
к settings.LOGIN_URL
.
Рассмотрим этот код JavaScript, который вызывает представление:
var getPage = function(pageNumber) {
$.ajax({
url: "/list_items/" + pageNumber + "/",
success: function(data) {
$("#list_container").html(formatData(data))
}
});
};
Предположим, settings.SESSION_COOKIE_AGE = 60
секунд.
Если aпользователь переходит на страницу 1, читает ее в течение 61 секунды, затем нажимает кнопку для страницы 2, декоратор Django login_required
обнаружит, что сеанс больше не активен, и вернет HttpResponseRedirect(settings.LOGIN_URL)
,что вызовет success
обратный вызов для получения страницы входа в HTML вместо закодированного в JSON списка.
Вот где это происходит.
Он вызывается user_passes_test
здесь.
Какой лучший способ справиться с этим?
Вот несколько вещей, о которых я подумал:
1.
success
Обратный вызов должен проверить ответ и посмотреть, получит ли он страницу входа любым способом (проверить, является ли тип содержимого html, проверить содержимое и т. д.).Но это означает, что мы должны обернуть все вызовы AJAX оболочкой обратного вызова следующим образом:
$.ajax({
url: "/list_items/" + pageNumber + "/",
success: sessionExpiryCallbackWrapper(function(data) {
$("#list_container").html(formatData(data))
})
});
Но это уродливо, и разработчики могут забыть делать это везде.
2.
Используйте $.ajaxComplete
для обработки всех запросов.
$.ajaxComplete(globalCompleteCallback);
$.ajax({
success: successCallback,
complete: completeCallback
});
Но это порядок вызовов:
successCallback(); // success is called before complete
completeCallback();
globalCompleteCallback(); // this is called after the local callback
Таким образом, мы перехватываем перенаправление только после сбоя successCallback и, возможно, с JSошибки из-за неверных данных, которые он получил.
3.
Если login_required
вернет 403 для запросов AJAX:
if not user.is_authenticated():
if request.is_ajax():
# send 403 to ajax calls
return HttpResponse403("you are not logged in")
else:
# regular code path
return HttpResponseRedirect(settings.LOGIN_URL)
Но login_required
просто использует user_passes_test
, который не делает этого.
user_passes_test
имеет много функций, так что это не такхорошая идея переопределить его.
Какой лучший способ справиться с таймаутами для вызовов AJAX?