Мы запускаем новый 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, чтобы облегчить их жизнь.
В первую очередь, 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.
Стандартный способ передачи списка значений в качестве параметров URL - повторить их:
http://our.api.com/Product?id=101404&id=7267261
Большинство кода сервера интерпретируют это как список значений, хотя многие из них имеют упрощенные значения, поэтому вам, возможно, придется искать.
Ограниченные значения также в порядке.
Если вам нужно отправить JSON на сервер, мне не нравится видеть его в URL (это другой формат). В частности, URL-адреса имеют ограничение по размеру (на практике, если не в теории).
Как я видел, некоторые из них выполняют сложный запрос RESTfully выполняется в два этапа:
POST
ваши требования к запросу, получение идентификатора (по существу, создание ресурса критериев поиска)GET
поиск, ссылающийся на указанный выше идентификаторВо-первых:
Я думаю, вы можете сделать это двумя способами.
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
Второй случай
Поиск с параметрами querystring - отлично. У меня возникло бы желание сочетать термины с символами AND и OR вместо []
.
PS Это может быть субъективным, поэтому делайте то, что вам удобно.
Причина размещения данных в URL-адресе заключается в том, что ссылка может быть вставлена на сайт/совместно используется пользователями. Если это не проблема, обязательно используйте JSON/POST.
EDIT: при отражении я думаю, что этот подход подходит к сущности с составным ключом, но не к запросу для нескольких объектов.
/
не должен присутствовать, поскольку URI обращается к ресурсу, а не к коллекции.
Я соглашусь с ответом nategood, поскольку он завершен, и он, похоже, понравился вашим потребностям. Хотя, я хотел бы добавить комментарий к идентификации нескольких (1 или более) ресурсов таким образом:
http://our.api.com/Product/101404,7267261
При этом вы:
Усовершенствовать клиентов
заставляя их интерпретировать ваш ответ как массив, который мне противоположен интуитивно, если я сделаю следующий запрос: http://our.api.com/Product/101404
Создание избыточных API-интерфейсов с одним API для получения всех продуктов и одного выше для получения 1 или многих. Поскольку вы не должны показывать пользователю более 1 страницы деталей для UX, я считаю, что более 1 идентификатор был бы бесполезным и использовался исключительно для фильтрации продуктов.
Возможно, это не так проблематично, но вам придется либо обработать эту серверную сторону, возвращая единый объект (проверяя, содержит ли ваш ответ один или несколько), либо позволяют клиентам управлять им.
Пример
Я хочу заказать книгу от Amazing. Я точно знаю, какая именно книга, и я вижу ее в списке при навигации для книг ужасов:
После выбора второй книги я перенаправляется на страницу с подробным описанием части книги:
--------------------------------------------
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=
Конечно, это мое мнение, поскольку оно не навязывается.
[]
не всегда поддерживается (и, несмотря на то, что он распространен, может даже нарушать спецификацию URI). Некоторые HTTP-серверы и языки программирования предпочитают просто повторять имя (например,productType=value1&productType=value2
).