RESTful API дизайн. Что я должен вернуть, если нет строк?

50

В настоящее время я пишу API для социальной сети с Slim Framework. Мой вопрос: каковы лучшие практики, когда в структуре json нет строк для возврата?

Допустим, этот вызов / v1 / get / movies возвращает 2 строки из таблицы названий фильмов:

[
    {"name": "Ghostbusters"},
    {"name": "Indiana Jones"}
]

Но затем я вызываю / v1 / get / books и в этой таблице нет строк. Должен ли я просто вернуть пустую структуру?

[
]

... или было бы лучше сообщение и код ошибки?

[
    "errors": {
        "message": "no matches found",
        "code": 134
    }
]

Какая практика лучше? (API будет использоваться в приложениях iOS и Android) Спасибо!

Андрес СК
источник
3
Для меня это похоже на вопрос, является ли ноль на самом деле величиной.
шарфридж
16
Ваш пример сломан. У вас не может быть объектов json с дублирующимися ключами. То, что вы ищете, это массив, то есть[{"name": "..."}, {"name":"..."}]
Martin Wickman
@MartinWickman Извините за это, я только что исправил это.
Андрес СК
8
@andufo, на самом деле, ты не ...
avakar
25
Если ваше приложение предназначено для RESTful, то почему глагол / метод "get" является частью вашего URI конечной точки?
user50849

Ответы:

46

Обычно я бы возвращал количество записей в результате в виде метаданных. Я не уверен, что это нормальная практика REST, но это не слишком много дополнительных данных, и это очень точно. Обычно для многих сервисов существует нумерация страниц, нецелесообразно сразу возвращать огромный набор результатов. Лично я раздражен, когда есть нумерация страниц для маленьких наборов результатов. Если это пусто, возврат number_of_records : 0и книги как пустой список / массив books : [].

{
    meta: {
        number_of_records : 2,
        records_per_page : 10,
        page : 0
    },
    books : [
        {id:1},
        {id:27}
    ]
}

РЕДАКТИРОВАТЬ (несколько лет спустя): Ответ от Мартина Викмана намного лучше, вот «краткое» объяснение, почему.

При работе с нумерацией страниц всегда помните о возможности изменения содержимого или порядка. Например, поступает первый запрос, 24 результата, вы возвращаете первые 10. После этого вставляется «новая книга», и теперь у вас есть 25 результатов, но с исходным запросом он будет упорядочен на 10-м месте. Когда первый пользователь запрашивает 2-ю страницу, он не получит «новую книгу». Существуют способы решения таких проблем, например, предоставление «идентификатора запроса», который должен быть отправлен с помощью следующих вызовов API, затем возврат следующей страницы из «старого» набора результатов, который должен быть каким-то образом сохранен и привязан к «идентификатору запроса». Альтернативой является добавление поля типа «список результатов изменен с первого запроса».

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

Если у вас слишком много данных для одновременной обработки , рассмотрите возможность возврата «списка идентификаторов» со всеми результатами и подробностями для некоторого фрагмента этого списка и предоставьте вызовы API multi_get / get_by_id_list для ресурса.

grizwako
источник
1
Да, я удивляюсь, почему за этого не голосуют так же высоко, как за другого. Мне это нравится больше, так как он дает пустой список (который должен был быть списком, верно?), Который можно слепо перебирать без особых условий, а также счет метаданных как способ сказать: «Нет, мы не сделали» Если вы ошиблись, на самом деле было 0 результатов ".
Изката
1
-1 потому что booksпараметр является объектом, но «books» подразумевает более одного, а более одного подразумевает массив. Метаданные - это круто, и почти, в конечном счете, я ожидаю, что коллекция книг будет представлять собой массив объектов книги; если нет книг, просто дайте мне пустой массив
Чарльз Спрейберри
9
Проблема в том, что добавление «number_of_records» не дает больше информации, оно просто добавляет избыточность и увеличивает сложность. Чтобы сообщить об ошибке, верните подходящий http-код + что-то в теле.
Мартин Уикман,
1
@cspray books - это список, как указала Изката, моя опечатка.
Гризвако
2
@MartinWickman Я не хотел загрязнять исходный ответ дополнительными метаданными, но, по моему опыту, многие службы не сразу возвращают все данные, но «разбивают на страницы».
grizwako
105

Ваш пример сломан. У вас не должно быть объектов json с дублирующимися ключами. То, что вы ищете, это массив с объектами фильма, например:

 [
    {"name": "movie1"}, 
    {"name": "movie2"}
 ]

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

[]

С другой стороны, если вы пытаетесь получить конкретный ресурс фильма, GET api/movie/34а этот фильм не существует, верните 404 с подходящим (закодированным в json) сообщением об ошибке в теле

Мартин Викман
источник
1
+1 Это действительно JSON согласно json_xs.
10
15

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

Поэтому, когда у вас есть записи, вы бы вернулись:

    [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]

И когда у вас нет записей, вы бы вернулись:

    [

    ]
Девдатта Тенгше
источник
14

Если вы успешно выполнили операцию, но ей ничего не нужно возвращать, например, пустую карту {}или пустой массив, []я бы предпочел ответить кодом ответа 204, вот выдержка из спецификации определений кода состояния HTTP :

Сервер выполнил запрос, но ему не нужно возвращать тело объекта, и он может захотеть вернуть обновленную метаинформацию. Ответ МОЖЕТ включать новую или обновленную метаинформацию в форме заголовков объекта, которые, если они присутствуют, ДОЛЖНЫ быть связаны с запрошенным вариантом.

Если клиент является агентом пользователя, он НЕ ДОЛЖЕН изменять представление своего документа по сравнению с тем, что вызвало отправку запроса. Этот ответ в первую очередь предназначен для того, чтобы ввод данных для действий происходил, не вызывая изменений в активном представлении документа агента пользователя, хотя любая новая или обновленная метаинформация ДОЛЖНА применяться к документу, который в данный момент находится в активном представлении агента пользователя.

Ответ 204 НЕ ДОЛЖЕН включать тело сообщения и, таким образом, всегда заканчивается первой пустой строкой после полей заголовка.

По сути, я рекомендую использовать 204 в приложениях RESTful через HTTP, когда нечего возвращать.

Давид Сергей
источник
4
Я согласен с комментарием @ avakar к другому ответу здесь. Если клиент пытается получить доступ к / v1 / get / movies / 1, он должен вернуть 404, если нет фильмов, идентифицируемых по 1. Просто / v1 / get / movies должно вернуть 200, даже если фильма нет. Но 204 не подходит, потому что он предназначен для действий ввода.
imel96
7
Другая проблема, связанная с этим решением, состоит в том, что ему обычно требуется специальный код на клиенте: если ответ представляет собой пустой список, его можно проанализировать как JSON, как обычный ответ. Если ответ является пустым телом, синтаксический анализатор JSON, скорее всего, будет жаловаться (поскольку документ emtpy не является допустимым JSON), поэтому клиенту необходим дополнительный код для проверки HTTP 204 и пропуска анализа.
слеске
7
Я считаю, что это неверное прочтение намерения 204. 204, по-видимому, предназначено не для операций, которые ожидали содержимого и не смогли его найти, а скорее для операций, которые были успешными и не имели ожидаемого возврата . Из Википедии: «Сервер успешно обработал запрос, но не возвращает никакого содержимого. Обычно используется как ответ на успешный запрос на удаление».
XML
10

Была проделана разумная работа по созданию стандартизированного формата JSON API .

Следование принципам этой спецификации означает, что все возвращаемые ресурсы должны фактически быть «коллекциями» (даже если включен только один ресурс). Следование этому будет означать, что ваш вызов /v1/get/moviesвернется:

{
    "movies": [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]
}

Ваш вызов /v1/get/books(который возвращает ноль ресурсов) вернет:

{
    "books": []
}
Тим Блэр
источник
5

Для вашего конкретного примера я бы порекомендовал, чтобы / v1 / get / books возвращал HTTP 200 с пустым массивом.

Если я правильно читаю ваш пост, ваш API собирается собирать книги. Говоря метафорически, у вас есть книжная полка для книг, стойка для DVD для фильмов и, возможно, другие контейнеры, о которых вы здесь не упоминали. Поскольку вы намерены собирать книги, / v1 / get / books - это ваша книжная полка. Это означает, что там есть действительный ресурс - список книг, который в вашем конкретном примере оказался пустым.

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

Причина, по которой я не предлагаю возвращать HTTP 204, заключается в том, что это означает, что «Нет содержимого» является обычным делом: выполнение этого действия на этом ресурсе обычно ничего не возвращает. Вот почему он обычно используется в качестве ответа на запросы DELETE, например: характер удаления обычно означает, что возвращать нечего. Аналогичный случай, когда он используется для ответа на запросы с семейством заголовков If-Modified: вы хотели контент только в том случае, если ресурс изменился, но это не так, поэтому я не дам вам никакого контента.

Но я утверждаю, что для получения пустой, но действительной коллекции HTTP 204 не имеет смысла. Если бы в коллекции были элементы, то правильным представлением был бы массив этих данных. Поэтому, когда там нет данных (но коллекция действительна), правильным представлением является пустой массив.

Ложка
источник
5

Вы действительно должны сделать только одну из двух вещей

Либо верните 200 (OK)код состояния и пустой массив в теле.

Или верните 204 (NO CONTENT)код состояния и НЕТ тела ответа.

Мне вариант 2 кажется более технически правильным и соответствует принципам REST и HTTP.

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

Vihung
источник
3

Я видел оба случая в производственных условиях. Какой из них вы выберете, зависит от того, кто будет использовать API. Если они хотят знать, почему список пуст, или быть уверены, что список действительно пустой и при его получении не было ошибок, то вам следует прикрепить объект «ошибки». Если им все равно, возвращайте пустой список. Я бы выбрал второй подход, поскольку он охватывает больше потребностей, чем первый.

devnull
источник
3

Первое, что нужно учитывать, так как вы создаете RESTful API, - это вернуть соответствующий код ответа. И более подходящий код ответа, чтобы сообщить, что запрос прошел нормально, но запрошенный ресурс в данный момент недоступен, - это почтенный 404.

Если вы разрабатываете свой API таким образом, что он всегда возвращает разумный код ответа, вам может даже не понадобиться возвращать тело, когда ресурс не найден. Тем не менее, возвращение тела, особенно читаемого человеком, не повредит.

Здесь нет «лучшей практики», оба ваших примера произвольны, просто выберите один и будьте последовательны . Разработчики ненавидят сюрпризы, если они /v1/get/moviesвозвращаются, {}когда нет фильмов, тогда мы ожидаем, /v1/get/actorsчто они вернутся, {}когда нет актеров.

Яннис
источник
1
Возвращение 404 действительно правильное решение, но, к сожалению, никто этого не делает, в том числе и я.
Рибальд Эдди
1
если у вас есть сложные ответы, и только их части пусты, возврат 404 приведет пользователя в замешательство.
devnull
5
Я бы не согласился с сообщением 404. А 404 я бы интерпретировал как «ресурс не существует» и беспокоился бы, если у меня что-то не так с моим URL или чем-то еще. Если бы я попросил список фильмов и получил бы 404, я бы подумал, что кино вообще не существует. «204 Нет содержимого» может быть более подходящим.
Торстен Мюллер
8
Хорошо, вещь "без тела" убьет это. Но: «Код статуса 4xx предназначен для случаев, когда клиент, похоже, допустил ошибку». Но на стороне клиента не было ошибок. Таким образом, 404 дает неверную информацию. Либо отправьте 204 без тела, либо скажите, что все в порядке, и отправьте пустой список.
Торстен Мюллер
9
Вы просите список книг, возврат 404 будет означать, что список не существует, а не то, что он пуст. Возвращение 200 вместе с пустым списком кажется мне единственным разумным вариантом.
Авакар
1

Я не думаю, что правильный ответ - тот, который отмечен.

Ответ, предоставленный nirth, должен быть лучшим в истинном сценарии REST. Ответ тела должен быть пустым, а код статуса http: 204; ресурс существует, но в то время у него нет содержимого: он пуст.

REST HTTP_Status_Codes

jmmtcarvalho
источник
1

Я рекомендую 200+ пустой массив, так как он упрощает обработку всеми клиентами API. 200 + массив означает «я вернул все данные, которые есть». Как для кода, доставляющего данные, так и для кода, обрабатывающего их, количество элементов не имеет значения.

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

Было предложение вернуть статус 204 + пустое тело. Это означает , что вы вынуждаете каждый одного клиента в состояние 204 процесса правильно. Более того, вы заставляете их обрабатывать не-JSON ответы! Я надеюсь, что все понимают, что только потому, что запрос получил ответ, это не означает, что ответ пришел с сервера при использовании http, и просто проверка того, что ответом является JSON, обрабатывает многие из этих случаев.

gnasher729
источник
0

Я бы "Это зависит".

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

Я уверен, что вы можете спорить со спецификой моего примера, но вы поняли ...

Johnb
источник
0
  • Прежде всего, если getв вашем URL нет RESTful, GET подразумевается методом HTTP.
  • Если вы запрашиваете коллекцию, как GET api/moviesвернуть 200 OKс пустым массивом[] .
  • Если вы запрашиваете определенный фильм типа GET api/movies/1(где 1идентификатор), а он не существует, верните a 404 Not Found.

Почему? Вы запрашиваете ресурсы . Когда вы запрашиваете коллекцию, сам ресурс (коллекция) существует. Поэтому 404неправильно. Но если вы запрашиваете определенный фильм, а он не существует, запрошенный ресурс не существует, и вы должны вернуть a 404.

felixfbecker
источник
-2

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

китайско
источник