REST API Рекомендации: как принять список значений параметров в качестве входных данных

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

Прямо сейчас наш API очень ориентирован на JSON (возвращает только JSON). Дискуссия о том, хотим ли мы / нужно ли возвращать XML, является отдельной проблемой.

Поскольку наш выход API ориентирован на JSON, мы пошли по пути, где наши входы немного ориентированы на JSON, и я подумал, что это может быть удобно для некоторых, но в целом странно.

Например, чтобы получить несколько подробностей о продукте, по которым можно одновременно извлечь несколько продуктов, у нас есть:

http://our.api.com/Product?id=["101404","7267261"]

Должны ли мы упростить это как:

http://our.api.com/Product?id=101404,7267261

Или удобен ввод JSON? Больше боли?

Мы можем хотеть принять оба стиля, но действительно ли эта гибкость вызывает больше путаницы и головных болей (ремонтопригодность, документация и т. Д.)?

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

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

Мы не обязательно хотим помещать типы фильтров (например, productType и color) в качестве имен запросов, например:

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

Потому что мы хотели сгруппировать все входные данные фильтра.

В конце концов, действительно ли это имеет значение? Вполне вероятно, что существует так много утилит JSON, что тип ввода не имеет большого значения.

Я знаю, что наши клиенты JavaScript, делающие AJAX-вызовы API, могут по достоинству оценить входные данные JSON, чтобы сделать их жизнь проще.

6 ответов

Шаг назад

Прежде всего, REST описывает URI как универсально уникальный идентификатор. Слишком много людей увлеклись структурой URI, и какие URI более "спокойны", чем другие. Этот аргумент так же нелеп, как если бы сказать, что называть кого-то "Бобом" лучше, чем называть его "Джо", - оба имени делают "идентификацию человека". URI - это не что иное, как универсально уникальное имя.

Так что в глазах REST спорят о том, ?id=["101404","7267261"] более спокойный, чем ?id=101404,7267261 или же \Product\101404,7267261 несколько бесполезно.

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

Предложения

  1. Несколько URI для одного и того же ресурса и Content-Location

    Мы можем хотеть принять оба стиля, но действительно ли эта гибкость вызывает больше путаницы и головных болей (ремонтопригодность, документация и т. Д.)?

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

    Придерживаясь аналогии с именами, наличие нескольких URI похоже на использование псевдонимов для людей. Это вполне приемлемо и часто очень удобно, однако, если я использую псевдоним, я все еще, вероятно, хочу знать его полное имя - "официальный" способ обращения к этому человеку. Таким образом, когда кто-то упоминает кого-то по имени, "Nichloas Telsa", я знаю, что он говорит о том же человеке, которого я называю "Ник".

  2. "Поиск" в вашем URI

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

    Основное правило: если ваш URI содержит глагол, это может указывать на то, что что-то не так. URI идентифицируют ресурс, однако они не должны указывать, что мы делаем с этим ресурсом. Это работа HTTP или, в спокойном смысле, наш "единый интерфейс".

    Чтобы опровергнуть аналогию с именем, использование глагола в URI похоже на изменение чьего-либо имени, когда вы хотите с ним взаимодействовать. Если я общаюсь с Бобом, имя Боба не становится "BobHi", когда я хочу сказать ему "Привет". Точно так же, когда мы хотим "искать" продукты, наша структура URI не должна изменяться с "/Product/..." на "/Search/...".

Отвечая на ваш начальный вопрос

  1. относительно ["101404","7267261"] против 101404,7267261Мое предложение здесь состоит в том, чтобы избежать простоты синтаксиса JSON для простоты (то есть не требуйте, чтобы ваши пользователи выполняли кодировку URL-адресов, когда вам это не нужно). Это сделает ваш API более удобным. Еще лучше, как другие рекомендовали, идти со стандартом application/x-www-form-urlencoded формат, как это, вероятно, будет наиболее знакомым для ваших конечных пользователей (например, ?id[]=101404&id[]=7267261). Это может не быть "симпатичным", но Pretty URIs не обязательно означают Используемые URI. Однако, чтобы повторить мою начальную точку, хотя, в конечном счете, когда речь идет о REST, это не имеет значения. Не зацикливайтесь на этом.

  2. Ваш пример URI сложного поиска может быть решен почти так же, как пример вашего продукта. Я бы порекомендовал пойти application/x-www-form-urlencoded форматировать снова, поскольку это уже стандарт, с которым многие знакомы. Кроме того, я бы порекомендовал объединить два.

Ваш URI...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

Ваш URI после кодирования URI...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

Может быть преобразован в...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

Помимо того, что нужно избегать требования кодирования URL-адреса и делать вещи более стандартными, теперь он немного гомогенизирует API. Пользователь знает, что если он хочет получить продукт или список продуктов (оба они рассматриваются как один "ресурс" в терминах RESTful), он заинтересован в /Product/... URIs.

Стандартный способ передать список значений в качестве параметров URL - это повторить их:

http://our.api.com/Product?id=101404&id=7267261

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

Значения с разделителями тоже в порядке.

Если вам нужно отправить JSON на сервер, мне не нравится видеть его в URL (это другой формат). В частности, URL имеют ограничение по размеру (на практике, если не в теории).

Я видел, как некоторые выполняют сложный запрос RESTful, в два этапа:

  1. POST требования вашего запроса, получение идентификатора (по сути, создание ресурса критериев поиска)
  2. GET поиск, ссылающийся на вышеуказанный идентификатор
  3. При необходимости УДАЛИТЕ требования к запросу, но учтите, что они доступны для повторного использования.

Первый:

Я думаю, что вы можете сделать это 2 способами

http://our.api.com/Product/<id>: если вы просто хотите одну запись

http://our.api.com/Product: если вы хотите все записи

http://our.api.com/Product/<id1>,<id2>: как предположил Джеймс, это может быть вариант, так как то, что следует после тега Product, является параметром

Или мне больше всего нравится:

Вы можете использовать свойство Hypermedia как движок состояния приложения (HATEOAS) в RestFul WS и выполнять вызов http://our.api.com/Product это должно вернуть эквивалентные URL http://our.api.com/Product/<id> и позвони им после этого.

второй

Когда вам нужно сделать запросы по URL-адресам. Я бы предложил снова использовать HATEOAS.

1) позвонить http://our.api.com/term/pumas/productType/clothing/color/black

2) позвонить http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (Используя HATEOAS) Позвоните по адресу: http://our.api.com/term/pumas/productType/ -> получите URL-адреса всех возможных URL-адресов одежды -> позвоните по желанию (одежда и сумки) -> получить возможные цветовые URL -> назвать те, которые вы хотите

Вы можете проверить RFC 6570. Эта спецификация шаблона URI показывает много примеров того, как URL могут содержать параметры.

Первый случай:

Нормальный поиск продукта будет выглядеть так

http://our.api.com/product/1

Так что я думаю, что лучшая практика будет для вас, чтобы сделать это

http://our.api.com/Product/101404,7267261

Второй случай

Поиск с параметрами строки запроса - хорошо, как это. Я хотел бы объединить термины с AND и OR вместо использования [],

PS Это может быть субъективно, поэтому делайте то, что вам удобно.

Причиной размещения данных в URL является то, что ссылка может быть вставлена ​​на сайт / разделена между пользователями. Если это не проблема, во всех случаях используйте вместо этого JSON/ POST.

РЕДАКТИРОВАТЬ: Если подумать, я думаю, что этот подход подходит для сущности с составным ключом, но не для запроса для нескольких сущностей.

Я согласен с ответом nategood, так как он завершен, и он, кажется, удовлетворяет ваши потребности. Тем не менее, я хотел бы добавить комментарий об идентификации нескольких (1 или более) ресурсов таким образом:

http://our.api.com/Product/101404,7267261

При этом вы:

Сложите клиентов, заставив их интерпретировать ваш ответ как массив, что для меня будет нелогично, если я сделаю следующий запрос: http://our.api.com/Product/101404

Создайте избыточные API с одним API для получения всех продуктов и одним выше для получения 1 или многих. Поскольку вы не должны показывать пользователю более 1 страницы подробностей ради UX, я считаю, что иметь более 1 идентификатора было бы бесполезно и просто использовать для фильтрации продуктов.

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

пример

Я хочу заказать книгу из Amazing. Я точно знаю, что это за книга, и вижу ее в списке при переходе по книгам ужасов:

  1. 10 000 удивительных линий, 0 удивительных испытаний
  2. Возвращение удивительного монстра
  3. Давайте продублируем удивительный код
  4. Удивительное начало конца

После выбора второй книги я перенаправлен на страницу с подробным описанием книжной части списка:

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

Или на странице, дающей мне полную информацию только об этой книге?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

Мое мнение

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

/products/{id}
/products/{id}/specs/{name}

В тот момент, когда вам нужно более 1 ресурса, я бы предложил отфильтровать большую коллекцию. Для того же примера:

/products?ids=

Конечно, это мое мнение, поскольку оно не навязано.

Другие вопросы по тегам