API REST. Лучшая практика. Как принять список значений параметров в качестве входных данных.

Мы запускаем новый 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, чтобы облегчить их жизнь.

Ответ 1

Шаг назад

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

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

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

Предложения

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

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

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

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

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

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

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

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

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

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

  • Пример вашего обычного поиска 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/... URI.

Ответ 2

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

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

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

Ограниченные значения также в порядке.

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

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

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

Ответ 3

Во-первых:

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

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-адреса → называть те, которые вы хотите

Ответ 4

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

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

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

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

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

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

Поиск с параметрами querystring - отлично. У меня возникло бы желание сочетать термины с символами AND и OR вместо [].

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

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

EDIT: при отражении я думаю, что этот подход подходит к сущности с составным ключом, но не к запросу для нескольких объектов.

Ответ 5

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

Ответ 6

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

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

При этом вы:

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

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

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

Пример

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

  • 10 000 удивительных линий, 0 потрясающих тестов
  • Возвращение удивительного монстра
  • Позвольте дублировать удивительный код
  • Удивительное начало конца

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

--------------------------------------------
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}

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

/products?ids=

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