Подтвердить что ты не робот

Какой код статуса перенаправления HTTP лучше всего подходит для этого сценария REST API?

Я работаю над REST API. Ключевыми объектами ( "существительные" ) являются "элементы", и каждый элемент имеет уникальный идентификатор. Например. для получения информации об элементе с идентификатором foo:

GET http://api.example.com/v1/item/foo

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

POST http://api.example.com/v1/item/
hello=world&hokey=pokey

С помощью этой команды сервер проверяет, есть ли у нас элемент для информации hello=world&hokey=pokey. Итак, здесь есть два случая.

Случай 1: элемент не существует; он создан. Этот случай прост.

201 Created
Location: http://api.example.com/v1/item/bar

Случай 2: элемент уже существует. Здесь, где я борюсь... не знаю, какой лучший код перенаправления использовать.

301 Moved Permanently? 302 Found? 303 See Other? <Удаp > 307 Temporary RedirectЗабастовкa > Location: http://api.example.com/v1/item/foo

Я изучил описания Википедии и RFC 2616, и ни один из них не кажется идеальным. Вот конкретные характеристики, которые я ищу в этом случае:

Перенаправление является постоянным, так как идентификатор никогда не изменится. Таким образом, для эффективности клиент может и должен делать все будущие запросы на конечную точку ID напрямую. Это предполагает 301, так как остальные три должны быть временными.

Редирект должен использовать GET, хотя этот запрос является POST. Это предлагает 303, так как все остальные технически предполагается повторно использовать метод POST. На практике браузеры будут использовать GET для 301 и 302, но это REST API, а не веб-сайт, предназначенный для обычного пользователя в браузерах.

Он должен быть широко используемым и простым в использовании. В частности, 303 - это HTTP/1.1, тогда как 301 и 302 - HTTP/1.0. Я не уверен, насколько это проблема.

В этот момент я склоняюсь к 303 только для того, чтобы быть семантически правильным (используйте GET, не возвращайте POST) и просто сосать его на "временную" часть. Но я не уверен, что 302 будет лучше, поскольку на практике это было такое же поведение, как 303, но без HTTP/1.1. Но если я схожу с этой линии, то я задаюсь вопросом, является ли 301 еще лучше по той же причине плюс "постоянная" часть.

Мысли оценили!


Изменить: Позвольте мне лучше объяснить семантику этой операции "получить или создать" с более конкретным примером: сокращение URL. Это все равно гораздо ближе к моему приложению.

Для сокращений URL наиболее распространенной операцией является получение по идентификатору. Например. для http://bit.ly/4Agih5, бит .ly получает идентификатор 4Agih5 и должен перенаправить пользователя на соответствующий URL-адрес.

bit.ly уже имеет API, но это не действительно RESTful. Для примера позвольте мне составить более RESTful API. Например, запрос ID может возвращать всю информацию об этом (например, аналитика):

GET http://api.bit.ly/item/4Agih5

Теперь, если я хочу отправить новый URL-адрес в bit.ly, чтобы сократить, я не знаю ID моего URL-адреса заранее, поэтому я не могу использовать PUT. Вместо этого я бы использовал POST.

POST http://api.bit.ly/item/
url=http://stackoverflow.com/ (но закодирован)

Если bit.ly ранее не видел этот URL, он создаст для него новый идентификатор и перенаправит меня через 201 Создан для нового идентификатора. Но если он увидит этот URL-адрес, он все равно перенаправит меня без внесения изменений. Таким образом, я могу попасть в это место перенаправления, чтобы получить информацию/метаданные по сокращенному URL.

Как и этот пример сокращения URL-адресов, в моем приложении столкновения не имеют значения. Один URL-адрес сопоставляется с одним ID и что он. Поэтому не имеет значения, был ли URL укорочен раньше или нет; в любом случае, имеет смысл указать клиенту идентификатор для него, нужно ли сначала создать этот идентификатор или нет.

Поэтому я, вероятно, не буду менять этот подход; Я просто спрашиваю о лучшем методе перенаправления для него. Спасибо!

4b9b3361

Ответ 1

Я бы поспорил за 303. Предположим прямо сейчас hello = world & hokey = pokey однозначно идентифицирует элемент foo, но позже элемент foo hokey value изменяется на "smokey"? Теперь эти исходные значения больше не являются уникальным идентификатором для этого ресурса. Я бы сказал, что временная перенаправка подходит.

Ответ 2

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

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

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

Выбор для извлечения существующего объекта, когда пользователь попытался создать новый, кажется ошибочным.

Возможно, одной из альтернатив было бы вернуть 404 Bad запрос, если ресурс уже существует, и включить ссылку на существующий объект в теле объекта. Клиентское приложение может решить усвоить ошибку с плохим запросом и просто перейти по ссылке к существующей сущности и тем самым скрыть проблему от пользователя. Это будет выбор клиентского приложения, но, по крайней мере, сервер ведет себя четким образом.


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

С точки зрения клиента действительно не интересует, создает ли сервер новый сокращенный URL-адрес или оттягивает существующий. Фактически, нужно ли серверу генерировать новый идентификатор или нет, это полностью скрытая деталь реализации.
Скрытие процесса создания может быть очень ценным. Возможно, сервер может заранее предсказать, что вскоре будет запрошено множество коротких URL-адресов, связанных с событием, таким как конференция. Он мог предварительно генерировать эти URL-адреса в течение достаточно длительного времени, чтобы сбалансировать нагрузку на свои серверы.

Итак, исходя из этого предположения, почему бы просто не использовать

GET /ShortUrl?longUrl=http://www.example.org/en/article/something-that-is-crazy-long.html&suggestion=crazyUrl

Если URL уже существует, вы можете вернуться

303 See Other
Location: http://example.org/ShortUrl/3e4tyz

Если это раньше не было, вы могли бы получить

303 See Other
Location: http://example.org/ShortUrl/crazyurl

Я понимаю, что это похоже на то, что мы нарушаем правила GET, создавая что-то в ответ на GET, но я верю, что в этом случае нет ничего плохого в этом, потому что клиент не просил создать сокращенный URL-адрес и на самом деле все равно. Это идемпотент, потому что не имеет значения, сколько раз вы его называете.

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

Ответ 3

POST не поддерживает подход "искать или создавать". Сервер не может сказать клиенту: "Я бы это создал, но он уже существовал. Посмотрите здесь на существующую запись". Ни один из кодов 2xx не работает, потому что запрос не выполняется. Ни один из кодов 3xx не работает, потому что намерение не перенаправлять POST на новый ресурс. И 303 также не подходит, поскольку ничего не изменилось (см. 303 spec).

Что вы можете сделать, это предоставить форму или шаблон клиенту, который будет использоваться с PUT, который сообщает клиенту, как создать URI PUT. Если PUT приводит к 200, клиент знает, что ресурс существует, и если 201 возвращается, новый ресурс был создан.

Например:

Шаблон для URI: http://service/items/ {key}

PUT http://service/items/456

[данные]

201 Создан

или

PUT http://service/items/456

[данные]

200 Ok

Вы также можете создать "создать, но не заменять, если существует", используя "Нет-Матч":


PUT http://service/items/456
If-None-Match: *

[data]

412 Precondition failed 

Jan

Ответ 4

С точки зрения клиента, я бы подумал, что вы можете просто отправить 201 для случая 2 так же, как и для случая 1, что и для клиента, теперь запись "создана".

Ответ 5

HTTP 1.1. Spec (RFC 2616) предлагает 303:

303 См. Другие

Ответ на запрос можно найти в другом URI и  СЛЕДУЕТ извлекаться с использованием метода GET на этом ресурсе. Этот метод  существует, прежде всего, для обеспечения выхода активированного POST script перенаправить агент пользователя на выбранный ресурс. Новый URI не является  замените ссылку на первоначально запрошенный ресурс.