Какой код ответа подходит для этой ситуации?

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

Например, вы разблокируете /fields как часть шага 3 в обучении. Но что, если вы просто перейдете к /fields в адресной строке?

Я пытаюсь выяснить, какой код состояния будет лучшим для ответа.

Код 403 кажется идеальным, так как пользователю запрещен доступ к странице, пока он не разблокирует ее.
404 также имеет смысл, поскольку страница технически «не существует», пока она не будет разблокирована, а также не позволяет пользователям отличить страницу, которой не существует, и ту, которую они просто еще не разблокировали.

Но в обоих случаях у меня были некоторые пользователи, которые сообщали о проблемах с браузером, кэширующим результат 403/404 и не позволявшим им получить доступ к странице даже после ее разблокировки, если они полностью не очистили кеш.

Мне интересно, следует ли мне продолжать использовать 403 или 404, или мне следует использовать неиспользуемый код 4XX, такой как 442, с пользовательским статусным текстом, или даже в шутку отправить HTTP/1.1 418 I'm A Teapot в ответ на то, что пользователь ковыряется там, где их быть не должно.

Мне нужна веская, веская причина, по которой один вариант следует использовать вместо других.


person Niet the Dark Absol    schedule 19.02.2013    source источник
comment
Я не знаю правильного ответа, но следующий пост предлагает несколько интересных аргументов: stackoverflow.com/questions/3297048/   -  person RobJohnson    schedule 21.02.2013
comment
Почему бы просто не перенаправить на страницу, на которой им разрешено находиться? Также может быть показано всплывающее окно с сообщением о том, что эта функция еще недоступна.   -  person Alex Okrushko    schedule 22.02.2013
comment
Кстати, 401 Unauthorized было бы плохой идеей, потому что этот код состояния предназначен только для HTTP-аутентификации, и браузеры обрабатывают этот код состояния специально.   -  person nalply    schedule 23.02.2013


Ответы (3)


Можно было бы использовать tl;dr 409 Conflict, но, возможно, у вас проблемы с кэшированием. В этом случае сработает кэш-бастер для принудительной перезагрузки.

Длинное объяснение

Возможно, код состояния 409 Conflict имел бы смысл:

10.4.10 409 Конфликт

Запрос не может быть выполнен из-за конфликта с текущим состоянием ресурса. Этот код разрешен только в ситуациях, когда ожидается, что пользователь сможет разрешить конфликт и повторно отправить запрос. Тело ответа ДОЛЖНО включать достаточно информации, чтобы пользователь мог распознать источник конфликта. В идеале объект ответа должен содержать достаточно информации, чтобы пользователь или пользовательский агент могли решить проблему; однако это может быть невозможно и не требуется.

Конфликты чаще всего возникают в ответ на запрос PUT. Например, если использовалось управление версиями, а размещаемая сущность включала изменения в ресурс, которые противоречат тем, которые были сделаны в результате более раннего (стороннего) запроса, сервер может использовать ответ 409, чтобы указать, что он не может выполнить запрос. . В этом случае объект ответа, скорее всего, будет содержать список различий между двумя версиями в формате, определяемом Content-Type ответа.

Это имело бы смысл, потому что ресурс доступен только после того, как пользователь прошел обучение. До этого ресурс находится в «недействительном» состоянии. И пользователь может разрешить этот конфликт, пройдя обучение.

Позже я еще немного расследовал это дело и обнаружил, что дьявол кроется в деталях. Давайте прочитаем спецификацию для 403 Forbidden и 404 Not Found.

10.4.4 403 Запрещено

Сервер понял запрос, но отказывается его выполнять. Авторизация не поможет и запрос НЕ ДОЛЖЕН повторяться. Если метод запроса не был HEAD и сервер желает обнародовать, почему запрос не был выполнен, он ДОЛЖЕН описать причину отказа в сущности. Этот код состояния обычно используется, когда сервер не хочет точно раскрывать, почему запрос был отклонен, или когда никакой другой ответ не применим.

Важным является указание, что «запрос НЕ ДОЛЖЕН повторяться». Браузер, который никогда повторно не запрашивает страницу 403, может поступать правильно. Однако продолжим с 404:

10.4.5 404 Не найдено

Сервер не нашел ничего, соответствующего Request-URI. Никаких указаний на то, является ли состояние временным или постоянным, не дается.

[опущено]

Теперь у нас есть проблема! Зачем кешировать страницы 404, если спецификация позволяет им быть временными?

Возможно, у вас в настройках неправильно настроено кеширование для ваших 403 и 404 страниц. Если это так, обратитесь к этому ответу на StackOverflow. Он дает подробный ответ о кэшировании страниц 4xx.

Если вы не хотите возиться с кешированием заголовков, используйте так называемую очистку кеша и передайте системное время следующим образом (при условии, что PHP является вашим веб-языком):

<a href="/fields?<?php echo time(); ?>">

Это создает URL-адреса, такие как /fields?1361948122, увеличивающиеся каждую секунду. Это вариант решения, предложенного Маркусом А.

Я предполагаю, что строка запроса 1361948122 игнорируется вашим ресурсом. Если это не так, вместо этого передайте очиститель кеша в параметре строки запроса, например t=1361948122, и убедитесь, что параметр t не оценивается вашим ресурсом.

person nalply    schedule 22.02.2013

С точки зрения назначения кодов ошибок HTTP, я бы определенно выбрал 403 Forbidden, потому что страница существует (404 отсутствует), но пользователю запрещен доступ к ней на данный момент (и это ограничение связано не с конфликтом ресурсов, например с одновременным изменением, а из-за статуса учетной записи пользователя, т.е. 409, по моему мнению, также отсутствует). Другим разумным вариантом, основанным на его предполагаемой цели, мог быть 401, но, как уже было отмечено в его комментарии, этот код запускает некоторые, если не все, браузеры для отображения диалогового окна входа в систему, поскольку это подразумевает, что использование стандартного механизма веб-аутентификации может решить проблему. Так что тут точно не ваш вариант.

Две вещи кажутся немного «неуместными» в описании 403, поэтому позвольте мне остановиться на них:

  1. Авторизация не поможет... Это говорит только о механизме авторизации внутри протокола HTTP и предназначено для того, чтобы отличить 403 от 401. Это утверждение не относится ни к какой форме пользовательской авторизации или управлению состоянием сеанса. .
  2. ...запрос НЕ ДОЛЖЕН повторяться...: Запрос всегда должен отображаться в контексте сеанса, поэтому, если контекст сеанса пользователя изменится (он разблокирует функцию), а затем повторит попытку доступ к тому же ресурсу, то есть другой запрос, т.е. нет нарушения этого предложения.

Конечно, вы также можете определить свой собственный код ошибки, но, поскольку он, вероятно, не будет зарезервирован каким-либо официальным способом, нет никакой гарантии, что какой-либо производитель браузера не собирается намеренно или случайно использовать именно этот код для запуска определенной ошибки. (отладочное) действие. Это маловероятно, но не запрещено.

Хотя 418 тоже может подойти. :)

Конечно, если вы хотите специально скрыть потенциальную доступность функций, вы также можете решить использовать 404, поскольку это единственный способ не давать любопытным пользователям никаких подсказок.

Теперь к вашей проблеме с кэшированием:

Ни один из этих кодов состояния (403, 404, 409, 418) не должен заставлять браузер кэшировать страницу против вашей воли в большей степени, чем любой другой. Проблема в том, что многие браузеры просто пытаются кэшировать все как сумасшедшие, чтобы быть очень быстрыми. На мой взгляд, Опера здесь самая худшая. Я много раз рвал на себе волосы из-за этих вещей. ДОЛЖНО быть возможно решить все это с правильными настройками заголовка, но у меня были ситуации, когда либо браузер, либо сервер, либо какой-то промежуточный прокси-сервер решили игнорировать их и все равно сломать мою страницу.

Единственный верный способ, который я нашел до сих пор, который абсолютно точно гарантирует перезагрузку, — это добавить фиктивный параметр запроса, такой как /fields?t=29873, где 29873 — это число, уникальное для каждого запроса, который вы делаете в любом, возможно, релевантном шкалы времени. На сервере, конечно, вы можете просто игнорировать этот параметр. Обратите внимание, что недостаточно просто начать с 1, когда ваш пользователь впервые открывает вашу страницу, а затем подсчитывать последующие запросы, поскольку браузеры могут сохранять кэш при перезагрузке страницы.

Я делаю свою веб-разработку на Java (как на стороне сервера, так и на стороне клиента, используя GWT), и я использую этот код для создания фиктивных «чисел»:

private static final char[] base64chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.".toCharArray();
private static int tagIndex = 0;

/**
 * Generates a unique 6-character tag string that is guaranteed to not repeat
 * for about 400 days, if this function is, on average, not called more often
 * than twice every millisecond.
 * 
 * @return the tag string
 */
public static String nowTag() {
    int tag = (int) ((System.currentTimeMillis() >>> 5)); // adjust
    char[] result = new char[6];
    result[5] = base64chars[(tagIndex++) & 63];
    result[4] = base64chars[tag & 63];
    tag >>>= 6;
    result[3] = base64chars[tag & 63];
    tag >>>= 6;
    result[2] = base64chars[tag & 63];
    tag >>>= 6;
    result[1] = base64chars[tag & 63];
    tag >>>= 6;
    result[0] = base64chars[tag & 63];
    return new String(result); 
}

Он использует системные часы в сочетании со счетчиком, чтобы иметь возможность предоставлять до двух гарантированных уникальных значений каждую мс. Возможно, вам не нужна эта скорость, поэтому вы можете свободно изменять >>> 5, который я пометил как «настроить», в соответствии с вашими потребностями. Если вы увеличите его на 1, ваша ставка уменьшится в два раза, а временной диапазон вашей уникальности удвоится. Так, например, если вместо этого поставить >>> 8, можно генерировать примерно 1 значение каждые 4 мс, и значения не должны повторяться в течение 3200 дней. Конечно, эта гарантия того, что значения не будут повторяться, исчезнет, ​​если пользователь испортит системные часы. Но поскольку эти значения не генерируются последовательно, очень маловероятно, что вы нажмете одно и то же число дважды. Код генерирует 6-символьную текстовую строку (base64), а не десятичное число, чтобы URL-адреса были как можно короче.

Надеюсь это поможет. :)

person Markus A.    schedule 26.02.2013

Я чувствую, что нет необходимости выдавать код ошибки, несмотря на то, что вы просто отображаете сообщение вроде

Чтобы получить доступ к этой странице, вы должны иметь XX уровень или что-то смешное, например Возвращайся, когда вырастешь

с самим кодом 200-OK, поэтому проблем с кешем не будет, и цель также будет достигнута.

person Cyril    schedule 27.02.2013