Шаблон API клиент-сервер в REST (ненадежный вариант использования сети) - PullRequest
11 голосов
/ 16 февраля 2011

Давайте предположим, что у нас происходит взаимодействие клиент / сервер по ненадежной сети (отбрасывание пакетов). Клиент вызывает API RESTful сервера (через http через tcp):

  • выдача POST на http://server.com/products
  • сервер создает объект ресурса "product" (сохраняет его в базе данных и т. Д.)
  • сервер возвращает 201 Создано с заголовком Location "http://server.com/products/12345"
  • ! Пакет TCP, содержащий ответ http, отбрасывается, и в конечном итоге это приводит к сбросу соединения tcp

Я вижу следующую проблему: клиент никогда не получит идентификатор вновь созданного ресурса, но на сервере будет создан ресурс.

Вопросы: это поведение уровня приложения или должна заботиться об этом фреймворк? Как веб-фреймворк (и Rails в частности) должен справляться с такой ситуацией? Есть ли какие-либо статьи / технические документы на REST по этой теме?

Ответы [ 5 ]

5 голосов
/ 16 февраля 2011

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

Один из них заключается в том, что клиент может сгенерировать какой-то идентификатор запроса, такой как guid, который он включает в запрос.Если сервер получает запрос POST с дублированным идентификатором GUID, он может отклонить его.

Другой подход заключается в создании PUT вместо POST.Если вы не можете заставить клиента сгенерировать URI, вы можете попросить сервер предоставить новый URI с GET, а затем выполнить PUT для этого URI.

Если вы ищете что-то вроде «сделать POST idempotent», вы, вероятно, найдете кучу других советов о том, как это сделать.

4 голосов
/ 25 февраля 2011

Если нецелесообразно создавать дублирующиеся ресурсы (например, продукты с одинаковыми названиями, описаниями и т. Д.), То на сервере могут быть созданы уникальные идентификаторы, которые можно отслеживать по созданным ресурсам, чтобы предотвратить дублирование запросов.обработанный.В отличие от предложения Даррела о создании уникальных идентификаторов на клиенте, это также помешало бы отдельным пользователям создавать дубликаты ресурсов (что вы можете или не можете пожелать).Клиенты смогут различать «созданные» ответы и «дублированные» ответы по их кодам ответов (201 и 303 соответственно, в моем примере ниже).

Псевдокод для генерации такого идентификатора - в данном случае,хэш канонического представления запроса:

func product_POST
    // the canonical representation need not contain every field in
    // the request, just those which contribute to its "identity"
    tags = join sorted request.tags
    canonical = join [request.name, request.maker, tags, request.desc]
    id = hash canonical

    if id in products
        http303 products[id]
    else
        products[id] = create_product_from request
        http201 products[id]
    end
end

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

Во многих случаях также имеет смысл «истечь» эти уникальные хэши через некоторое время.Например, если вы хотите создать API-интерфейс для перевода денег, пользователь, переводящий одну и ту же сумму одному и тому же человеку на расстоянии нескольких минут, вероятно, указывает, что клиент никогда не получал ответ «успех».С другой стороны, если пользователь переводит одно и то же количество денег одному и тому же человеку один раз в месяц, он, вероятно, платит арендную плату.; -)

1 голос
/ 28 февраля 2011

Проблема, которую вы описываете, сводится к тому, чтобы избежать того, что называется double-add .Как уже упоминали другие, вам нужно сделать свои посты идемпотентными.

Это может быть легко реализовано на уровне платформы.Фреймворк может хранить кэш завершенных ответов.Запросы должны иметь уникальный запрос , чтобы любые повторные попытки обрабатывались как таковые, а не как новые запросы.

Если успешный ответ теряется на пути к клиенту, клиентуПовторите попытку с тем же уникальным запросом, а затем сервер ответит своим кэшированным ответом.

У вас останется долговечность кэша, как долго хранятся ответы и т. д. Один из подходов заключается в удалении ответов с сервера.кешировать через определенный промежуток времени, это будет зависеть от вашего домена приложения и трафика и может быть оставлено в качестве настраиваемого шага на платформе.Другой подход заключается в том, чтобы заставить клиента отправлять подтверждения.Подтверждения могут быть отправлены в виде отдельных запросов (обратите внимание, что они также могут быть потеряны) или в виде дополнительных данных, добавляемых на реальные запросы.

Хотя то, что я предлагаю, похоже на то, что предлагают другие, я настоятельно рекомендую вамчтобы сохранить этот уровень устойчивости сети, нужно выполнять только это, обрабатывать запросы / ответы отбрасывания и не позволять ему обрабатывать дублирующиеся ресурсы из отдельных запросов, что является задачей прикладного уровня.Объединение обеих частей объединит все функциональные возможности и не оставит четкое разделение обязанностей.

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

А для некоторых связанных с этим переживаний других перейдите здесь .

Удачи.

0 голосов
/ 01 марта 2011

Как указали другие респонденты, основная проблема здесь заключается в том, что стандартный метод HTTP POST не идемпотентен, как другие методы.В настоящее время предпринимаются попытки установить стандарт для идемпотентного метода POST, известного как Post-Once-Exactly, или POE.

Теперь я не говорю, что это идеальное решение для всех в описываемой вами ситуации, но если вы пишете и сервер, и клиент, вы можете использовать некоторыеидей от POE.Черновик находится здесь: http://tools.ietf.org/html/draft-nottingham-http-poe-00

Это не идеальное решение, поэтому, вероятно, оно и не появилось за шесть лет с момента представления черновика.Некоторые проблемы и некоторые умные альтернативные варианты обсуждаются здесь: http://tech.groups.yahoo.com/group/rest-discuss/message/7646

0 голосов
/ 16 февраля 2011

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

Единственное решение, которое я могу придумать: если вы знаете, какой клиент создал продукт, вы можете предоставить ему продукты, которые он создал, если это таит эту информацию.Если клиент никогда не свяжется с вами снова, вы не сможете передавать информацию о новом продукте.

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