Как правильно сделать сложный метод поиска RESTful?

44

Следуя принципам REST, я бы хотел создать метод GET для моего API, который выполняет поиск по некоторым критериям и возвращает результаты клиенту. Проблема в том, что критерии могут иметь до 14 параметров, один из которых - список сложных объектов, поэтому ...

  • Я даже не знаю, возможно ли кодировать / декодировать эти сложные объекты в / из параметров URL.

  • Я не рассчитывал, как долго может получиться URL, но я уверен, что он будет достаточно большим и, возможно, достигнет предела длины URL?

Кроме того, поиск должен показывать результаты в «реальном времени», я имею в виду, что каждый раз, когда пользователь что-то меняет в форме поиска, он должен видеть новые результаты, не нажимая кнопку «поиск».

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

anat0lius
источник
3
Кроме того (я отмечаю, что ни один из двух ответов на момент написания не упоминает часть реального времени), поиски в реальном времени обычно должны просто отправлять обновленный запрос снова и снова. Например, в то время как вы печатаете, вы будете посылать запросы на такие вещи , как search?q=t, search?q=te, search?q=testи так далее. Попробуйте ограничить частоту отправки запроса, чтобы не повредить вашему серверу. В качестве альтернативы вы также можете вернуть много информации и выполнить фильтрацию на стороне клиента. Это хорошо работает, если пользователь вводит широкие категории, которые могут сильно сузить круг.
Kat
2
Независимо от решения, обязательно обменивайтесь данными в технологически нейтральном формате. Поэтому не используйте внутреннее представление, иначе будет сложнее написать клиентов для вашего API.
Квеббл
В зависимости от ваших потребностей, эта библиотека может сделать работу: github.com/jirutka/rsql-parser . Он имеет расширение JPA, если вы используете JPA, или вы можете написать своего собственного посетителя для сопоставления с API вашей поисковой системы. И вы можете добавить свой собственный оператор.
Вальфрат

Ответы:

54

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

В любом случае, в ответе Нила я скучаю по еще одной вещи. Документация . Просто чтобы убедиться, что разработчики знают, что запросы POST /searchбезопасны.

Это сказал.

1. Дайте шанс ПОЛУЧИТЬ

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

1.1 Попробуйте кодировать строку запроса

Например, в базе 64. Даже javascript поддерживает кодировки базы 64 .

Вот как это работает:

  1. Создайте JSON со всеми фильтрами и нормализуйте его.
  2. Разобрать его в строку
  3. Кодируй это
  4. Отправьте закодированный JSON как запрос param ( /search?q=SGVsbG8gV29ybGQh....).
  5. На стороне сервера декодируйте параметр q .
  6. Десериализовать строку JSON

Ранее сделайте максимально длинную строку JSON, закодируйте ее и возьмите длину. Оцените, соответствует ли закодированная строка URL-адресу. Я реализовал следующий фрагмент на Fiddle.js для тестирования. (Я надеюсь, что это все еще работает) 1

Кодирование Base 64 является детерминированным и обратимым, поэтому шансов на столкновение нет.

С помощью закодированных запросов мы также можем сохранять результаты поиска в БД, добавлять закладки в URL, обмениваться ссылками и т. Д. И, конечно, нам не нужно экранировать / удалять строку.

1.2 Попробуйте с псевдонимами

Читая этот блог о том, как создавать REST API, я вспомнил еще одну альтернативу. Псевдонимы для общих запросов .

Я нахожу это интересным по следующим причинам

  • Сократите длину строки запроса. Это делает API чище и удобнее для пользователя

    ПОЛУЧИТЬ / тикеты /? Статус = закрыто и закрытоAt = xxx против GET / тикеты / недавно закрыто /

  • Комбинируется с большим количеством псевдонимов или большим количеством параметров запроса.

    ПОЛУЧИТЬ / билеты /? Статус = закрыто и закрытоAt = xxx & в течение = 30 минут против ПОЛУЧИТЬ / билеты / недавно закрытое /? В течение = 30 минут

  • Мы можем комбинировать псевдонимы с закодированными строками запроса

    ПОЛУЧИТЬ / тикеты /? Статус = закрыто & закрытоAt = xxx & в течение = 30 минут против GET / тикеты / недавно закрыто /? Q = SGVsbG8g ...


1: Я использовал JSON, но мы могли бы использовать другие форматы, как только мы сможем десериализовать его на стороне сервера.

LAIV
источник
2
Это практично и правильно. Стоит также отметить, что большинство языков программирования упрощают преобразование хеша в строку запроса, поэтому начинать с действия GET очень легко.
Алуан Хаддад
1
Я люблю Spring stackoverflow.com/questions/16942193/… Я не могу поверить, что это сработало с первой попытки: D. О длине URL меньше 1 КБ, хотя нам все еще нужно перебрать спецификации.
anat0lius
Затем перейдите с GET. Для простоты. С Spring MVC вы можете добиться того же отображения с помощью GET. Ищите Spring WebArgumentResolver ;-)
Laiv
Base64 увеличивает размер полезной нагрузки примерно на 4/3. Хотя urlencoding может сделать его 3/1 для специальных символов, запросы с наиболее безопасными символами будут иметь одинаковый размер. Есть ли другая причина использовать base64?
villasv
На самом деле, нет. Я просто не люблю (не) escape-URL. Избыток полезной нагрузки - это компромисс здесь. Он по-прежнему должен соответствовать максимальному размеру GET для каждого запроса. Вот почему я построил фрагмент. Для пользователя, чтобы попробовать. Когда я писал ответ, я отдавал приоритет веб-семантике деталям реализации. Весь смысл ответа «продолжай пытаться с GET». Найдите свой путь или используйте любой из них, которым я поделюсь с вами.
Laiv
13

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

Просто отправьте запрос POST с параметрами, предоставленными пользователем, чтобы получить необходимую информацию из серверной части. Я предполагаю, что вам не нужно ничего делать, кроме как выполнять поиск, поэтому у вас нет шансов вставить эту страницу. Просто добавьте / search в конец вашего URL, чтобы не рисковать столкнуться с вашей страницей / users, которая была бы RESTful.

Нил
источник
2
@LiLou_: Для этого требования есть только две реалистичные возможности: 1. Считать все данные в ваш интерфейс и выполнить там фильтрацию. Это может быть чрезмерно в необходимом объеме памяти. 2. Сделайте новый запрос к серверу для каждого изменения в критериях поиска. Неважно, является ли это запросом POST или GET, но задержка в сети может нарушить восприятие пользователем обновлений в режиме реального времени.
Барт ван Инген Шенау
2
Я бы согласился не согласиться, POST означает что-то другое семантически. Я бы предложил пойти с GET и передать все данные фильтра в параметре запроса, если слишком много параметров, то это ошибка на уровне приложения.
CodeYogi
2
@ КодYogi иногда клиент не дает вам места для обсуждения. Я реализовал Excel-подобные страницы с 40-50 столбцами, каждый из которых имеет свой собственный фильтр. И сортируемый конечно. В любом случае, все еще возможно с GET, но это может показаться не слишком модным
Laiv
1
@Laiv в этом случае нужно серьезно обсудить, потому что POST предназначен для изменения состояния сервера. Варианты использования, подобные этому, не являются исключительными, поэтому следует позаботиться о них без взломов.
CodeYogi
2
В этих случаях документация обязательна. У меня была серьезная дискуссия с клиентами относительно удобства использования их приложений. Позже я оказался прав, потому что конечный пользователь согласился со мной. Однако иногда вам приходится выбирать битвы.
Laiv
0

Это полностью зависит от того, какая у вас модель API: ни как, ни как глагол.

Если API отсутствует, вы можете получить список объектов следующим образом:

GET: /api/v1/objects

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

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

Некоторые платформы поддерживают собственный преобразователь параметров (например, Spring MVC), и вы можете конвертировать параметры в объект.

Мостафа
источник