Как мне работать с разными запросами, которые соответствуют одному и тому же ответу? - PullRequest
4 голосов
/ 30 апреля 2010

Я разрабатываю веб-сервис. Запрос идемпотентен, поэтому я выбрал метод GET. Ответ относительно дорог для вычисления и не мал, поэтому я хочу, чтобы кэширование (на уровне протокола) было правильным. (Не беспокойтесь о воспоминаниях с моей стороны, я об этом уже говорил; мой вопрос здесь также касается всего Интернета.)

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

GET /service?mandatory_parameter=some_data HTTP/1.1
GET /service?mandatory_parameter=some_data;optional_parameter=default1;another_optional_parameter=default2;yet_another_optional_parameter=default3 HTTP/1.1

Тем не менее, я думаю, что клиенты не знают об этом и будут рассматривать их отдельно и, следовательно, тратить впустую кэш-память. Что я должен сделать, чтобы не нарушать золотое правило кэширования ?

  1. Составьте каноническую форму, задокументируйте ее (например, все параметры требуются в конце концов и должны быть отсортированы в определенном порядке) и верните ошибка клиента , если требуемая форма не выполняется?
  2. Вместо ошибки перенаправить навсегда к канонической форме запроса?
  3. Или этого достаточно, чтобы не обращать внимания на то, как выглядит запрос, и просто ответить тем же ETag на те же ответы?

Ответы [ 4 ]

4 голосов
/ 07 мая 2010

Во-первых, не используйте точки с запятой в качестве разделителя в строке запроса.Вы должны использовать ? для начала строки запроса и & для разграничения пар переменная / значение. RFC 3986 прямо не говорит, что вы должны использовать &, но подавляющее большинство существующего кода использует этот разделитель из-за прецедента application/x-www-form-urlencoded.

Во-вторых, выверно, в том, что параметры в строке запроса приводят к другому URI, и, таким образом, что касается кэшей, к другому ресурсу.Предполагая, что вам нужна оптимальная производительность кэширования, если вы знаете, что был задан необязательный параметр, и его включение не является необходимым и не влияет на представление, которое будет передаваться, вам следует выполнить перенаправление в каноническое представление, в котором параметр не указан.(т. е. необязательный параметр задается со значением, которое установлено в значение по умолчанию. Например, если у вас есть http://example.com:80/, вы можете нормализовать значение до http://example.com/, поскольку 80 является значением по умолчанию для порта с HTTP.Вы можете сделать то же самое для параметров запроса, так как вы управляете пространством URI.) Если у вас есть включенные параметры (необязательные или иные), которые отображаются в порядке, отличном от канонического, вы также должны перенаправить их.Перенаправление 301 было бы предпочтительным, если вы знаете, что отношения между URI будут стабильными.В противном случае выполните перенаправление 302/307 в зависимости от ситуации.Я бы порекомендовал определить вашу каноническую форму так же, как это делает OAuth : сортировать каждый параметр по алфавиту, сначала по ключу, а затем по значению.Другие операции нормализации также помогут здесь.RFC 3986 имеет целый раздел нормализация URI , который будет иметь отношение к вам.Эта методика действительно будет работать только для GET, и перенаправления на PUT / POST / DELETE обычно не рекомендуются.

В-третьих, ETag - это здорово, и они обеспечивают значительное улучшение производительности, если хорошо реализованы как клиентом, так и сервером,Однако, к сожалению, редко обе стороны делают это правильно.То же самое для последнего изменения.Вы должны следовать им, потому что экономия ресурсов процессора и полосы пропускания значительны, когда он работает, но сами по себе они недостаточны.Другие заголовки, такие как Cache-Control, также часто необходимы.Стоит ознакомиться с Разделом 13 RFC 2616 , если вы планируете вдаваться в подробности этого материала.

Наконец, слово предупреждения - есть проблема с этими перенаправлениямиВы должны знать: клиенты, пытающиеся получить доступ к вашим ресурсам, часто могут быть перенаправлены в другие места.Это приводит к накладным расходам, которые дают вам общую экономию, только если клиенты делают последующие запросы к тому же ресурсу, сохраняя состояние, чтобы избежать последующего перенаправления.Если вы не открыли эталонную клиентскую реализацию, которая использует преимущества оптимизации кэширования, вы никогда не сможете воспользоваться этими настройками.

1 голос
/ 04 мая 2010

Я бы выбрал вариант (2) в вашем списке - я бы сделал запрос RESTful, а не как RPC.

т.е. в этом случае, если вы сделаете все параметры частью пути запроса:

/ услуги / mandatory_parameter / some_data / optional_parameter / Default1 / another_optional_parameter / default2 / yet_another_optional_parameter / default3

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

В этот момент у вас есть одна каноническая форма для URI, и кэширование будет работать как обычно / ожидаемо.

Это означает, что каждая комбинация параметров будет кэшироваться отдельно (как 301), однако это нормально, так как неканонические запросы будут иметь независимую политику кэширования для полного запроса и клиентов, которые беспокоятся о дополнительном раунде Поездка может заполнить все параметры самостоятельно.

Ваш вариант (3) не будет работать так, как вы ожидаете - каждая форма будет кэшироваться независимо, поскольку они являются разными URI.

Следует также отметить, что многие нисходящие кэши / программы не будут кэшировать ваш ответ вообще из-за параметров запроса, поэтому я предлагаю превратить его в «правильный» ресурс. .

0 голосов
/ 08 мая 2010

Я задал почти тот же вопрос для меня несколько месяцев назад. Мой ответ я опишу на примере моей реализации.

На стороне сервера у меня есть служба WFC, которая получает запросы в одной из следующих форм

GET /Service/RequestedData?param1=data1&param2=data2…
GET /Service/RequestedData/IdOfData?param1=data1&param2=data2…
PUT /Service/RequestedData/IdOfData // with param1=data1&param2=data2… in body
POST /Service/RequestedData/IdOfData // with param1=data1&param2=data2… in body
DELETE /Service/RequestedData/IdOfData

Таким образом, запросы находятся в REST для, но GET запросы имеют некоторые необязательные параметры. Особенно эта часть является портом вашего интереса.

Поскольку WFC поддерживает шаблоны URL, прототип функций, отвечающих на запрос клиента, выглядит как

[WebGet (UriTemplate = "RequestedData?param1={myParam1}&param2={myParam2}",
         ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
MyResult GetData (string myParam1, int myParam2);

Все запросы, как

GET /Service/RequestedData?param1=&param2=data2
GET /Service/RequestedData?param2=data2&param1=
GET /Service/RequestedData?param2=data2

будет сопоставлен с тем же вызовом со стороны моей службы WCF. Так что у меня на одну проблему меньше.

Теперь в начале реализации каждого метода, ответ на HTTP GET запрос я задаю в заголовке HTTP " Cache-Control: max-age = 0 ". Это означает, что клиент всегда пытается проверить кэш браузера клиента, и никакие запросы ajax не будут легко отвечать из локального кэша, как это может сделать Internet Explorer.

Далее я всегда вычисляю ETag на основе моих данных. Точный алгоритм является предметом отдельного обсуждения, но важно то, что во всех ответах на HTTP GET запросы существуют ETag в заголовке HTTP.

Таким образом, клиенты каждый раз проверяют свой локальный кеш и отправляют GET-запрос на сервер. Они отправляют ETag из локального кэша внутри HTTP-заголовка "If-None-Match". Сервер вычисляет ETag, в котором есть данные, которые будут отправляться обратно на этот запрос GET. Это ETag данных, таких же, как на клиентском сервере запросов отправлять ответный ответ с пустым телом и кодом «304 Not Modified» назад. В этом случае браузер выдает данные из локального кэша.

Если тот же клиент по неизвестной причине создаст новую версию запроса URL, которая будет интерпретироваться из веб-браузера как новый URL-адрес, тогда веб-браузер не найдет старый ответ сервера в локальном кэшируйте и отправьте еще один и тот же запрос на сервер. Это настоящая проблема? Сервер отправляет данные еще раз. Если у вас есть кэширование на стороне сервера, вы можете сделать немного больше оптимизации. В большинстве случаев URL-адрес GET-запросов будет создаваться клиентским JavaScript, поэтому у вас никогда не будет такой ситуации.

Вычисление ETag и установка заголовков "Cache-Control: max-age=0" и Etag, а также установка кода "304 Not Modified" должны выполнять функцию WFC, но это очень просто.

Самое важное, что моя реализация вычисления ETag не так обширна, как получение целых данных с сервера базы данных и отсюда кеша MD5 для вычислений. Я постоянно использую rowversion тип данных в каждой строке данных в базе данных SQL Server. Этот rowversion является ничем иным, как счетчиком изменений в базе данных. При изменении строки данных rowversion значение в соответствующей строке будет увеличиваться. Таким образом, если сделать оператор SELECT из максимального значения rowversion, и это значение не изменилось по сравнению с предыдущими запросами, можно быть уверенным, что данные не изменились за период времени. Алгоритм расчета ETa g должен быть чувствителен только к удалению данных из таблицы. Но это тоже решенная проблема. Еще немного об этом вы можете прочитать в Обработка параллелизма транзакции Sql .

Я не хочу предлагать мой ETag расчет как лучший выбор, я только хочу сказать, что вычисление ETag может быть намного дешевле, чем вычисление MD5 из всех данных.

В случае ошибок Сервер выдает исключение, которое будет сопоставлено с HTTP-кодом, который я определю в операторе throw. В качестве тела WFC отправляет стандартный объект JSON {"description":"My error text"}. Также возможен пользовательский объект ошибки (см. Включено ли исключение WebProtocolException в .net 4.0? ). На стороне клиента я использую jQuery, и в соответствующем jQuery.ajax внутри обработчика события ошибки сообщение об ошибке будет декодировано и отображено пользователю.

Итак, моя рекомендация: использовать ETag вместе с "Cache-Control: max-age=0" для всех запросов HTTP GET. Для всех остальных запросов я рекомендую вам внедрить сервис RESTfull . Для реализации ошибки вы должны взглянуть на самый родной путь, который поддерживается программным обеспечением, используемым для реализации сервера и клиента, и использовать это.

ОБНОВЛЕНО : Чтобы очистить структуру URL, я должен добавить следующее. В моем сервисе основная часть, такая как GET /Service/RequestedData/IdOfData, описывает запрашиваемые объекты данных. Параметры param1=data1&param2=data2 в основном соответствуют информации о сортировке , пейджинге и фильтрации данных. Я использую активный плагин jqGrid для jQuery, и если конечный пользователь прокручивает сетку до следующей страницы, нажимает на заголовок столбца (сортировка данных) или, если он устанавливает фильтр в отношении функции поиска, все это следует для различных дополнительных к параметрам добавлен основной URL.

0 голосов
/ 07 мая 2010

Во-первых, хорошо, что вы выбираете GET, поскольку другие методы не имеют такой хорошей поддержки кэширования. Насколько я знаю, браузеры кэшируют URI по отношению к параметрам, поэтому я не думаю, что это хорошая идея - использовать каноническую форму.
Одна вещь, которую вы здесь не указали, это то, как эта служба будет использоваться. Если эти запросы сделаны из браузера (и мне кажется, что они, вероятно, отправлены из скрипта), запросы, вероятно, будут выглядеть одинаково, даже если их запрашивают более одного раза. Поэтому убедитесь, что любой генерирующий URI заканчивается одинаковым URI для одинаковых входных данных (удалите параметры по умолчанию или всегда включайте их).
Когда дело доходит до ETag, я рекомендую вам иметь это, хотя я хотел бы уточнить, как это работает; Вы получаете запрос, обрабатываете все ваши «дорогие вычисления», а затем, если был заголовок If-None-Match с тем же хешем (ETag), что и обработанный вами ответ, вы можете вернуть 304 Not-Modified. Таким образом, ETag используется для того, чтобы избежать передачи ответа, если он уже есть у клиента. (Конечно, вы можете реализовать кэширование на стороне сервера, но это лучше делать на основе входных параметров).
Для дальнейшего улучшения попаданий в кэш на стороне клиента вы можете установить правильные заголовки кэширования в своем ответе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...