Дизайн URL-адреса REST для более чем, меньше, чем операций
Мне сложно создать URL-адрес службы отдыха, которая может обрабатывать запросы клиентов на основе разбивки на страницы как один тип операций или запрашивать больше или меньше операторов в качестве другого типа операции. Например:
Разбивка:
GET /customers/0/100
На странице будет получено 100 клиентов.
Больше/меньше, чем:
Мне также нужен URL-адрес, чтобы получить клиентов с идентификатором больше n (например, скажем, 716). Как бы вы включили "больше" или "меньше" в URL-адрес. Я должен помнить, что символы " > " и "<" являются незаконными в URL-адресах. Я думаю, что этот дизайн URL выглядит странно:
GET /customers/greaterthan/716
GET /customers/lessthan/716
Я не могу использовать диапазон, поскольку это противоречило бы шаблону разбивки на страницы, указанному выше, и не является хорошим решением в любом случае, например:
GET /customers/716/999999999999
GET /customers/0/716
Я уверен, что мне не хватает чего-то очевидного - есть ли у кого-нибудь лучшее решение?
Ответы
Ответ 1
Pagination, morethan и lessthan, звучат как параметр запроса для меня, так как вы запрашиваете свой ресурс с этими параметрами. Поэтому вы должны сделать что-то вроде:
/customers? page = 1, или
/customers? page = 1 & gt = 716, или
/customers? page = 1 & gt; 716 & lt = 819
Вы можете даже ограничить размер страницы:
/customers? page = 1 & gt = 716 & lt = 819 & maxpagesize = 100
где gt означает больше (то же, что и при xml-экранировании), а lt меньше <.pdf >
Ответ 2
Если у вас есть несколько параметров и вам необходимо применить некоторые условия для каждого параметра, я рекомендую передать объект JSON в параметры.
Предположим, вы хотите выполнить условие для id
и page
:
/customers?id={"lt": 100, "gt": 30}&page={"start": 1, "size": 10}
В нем говорится, что я хочу, чтобы клиенты, у которых Id (s) меньше 100 и больше 30 на странице 1 и номер страницы из 10.
Итак, теперь, если вы хотите применить другое условие для других параметров, вы можете сделать это:
/customers?id={"lt": 100, "gt": 30}&children={"lt": 5, "gt": 2}&page={"start": 1, "size": 10}
и этот запрос означает, что клиенты с Id (s) меньше 100 и больше 30, дети меньше 5 и больше 2 в номере страницы 1 с размером страницы из 10.
Я настоятельно рекомендую вам прочитать этот документ о разработке API RESTful: http://blog.luisrei.com/articles/rest.html
Ответ 3
REST - это архитектурный стиль, который не следует рассматривать как специфичный для HTTP. Структура URI не является тем, что делает архитектуру RESTful.
С учетом сказанного вы, вероятно, захотите сделать свой URI таким образом, чтобы эти запросы приходили как параметры запроса в конце строки, например.
/customers?min=0&max=76
Ответ 4
@Юлио Фаерман:
Ну, проблема начинается, когда вы получаете несколько параметров. Представьте строку запроса для "Клиенты старше 18 лет и моложе 60 с более чем двумя детьми".
Вы можете определить любые параметры запроса, например:
/customers?min-age=19&max-age=59&min-children=3
в моем примере min и max являются целыми числами и являются инклюзивными. Вы можете изменить это, если хотите. Помните, что все, что содержится в URI, является частью идентификатора ресурса. Мое личное мнение состоит в том, что вещи после ?
приравниваются к предложениям в WHERE
части SQL-запроса (плюс ORDER BY
и LIMIT
, не показаны здесь):
SELECT * FROM customers WHERE age>=19 AND age<=59 AND children>=3
Edit:
Вместо префиксов min-
и max-
вы можете разрешить >
, <
(и, возможно, !
) в качестве окончательного символа имени параметра, поэтому вместо min-age
у вас есть параметр с именем age>
, который в сочетании со значением в строке запроса заканчивается похожим на age>=19
:-)
Очевидно, вы можете использовать этот трюк только тогда, когда сравнение имеет знак равенства.
Ответ 5
Предположим, вы собираетесь получать журналы своего сервера и предположите, что ваши данные выглядят следующим образом:
{
"protocol": "http",
"host": "example.domain.com",
"path": "/apis/classified/server/logs",
"method": "GET",
"ip": "::ffff:127.0.0.1",
"time": 1483066346338,
"usingTime": 12,
"status": 200,
"headers": {
"user-agent": "Mozilla/5.0 Chrome"
}
}
И вы хотите запросить такой способ, где
-
protocol
равно 'http'
.
-
host
равно 'example.domain.com'
, или не равно 'example.domain.me'
.
-
path
равно '/apis/classified/server/logs'
или нравится /*classified\/server*/
.
-
method
равно 'DELETE'
или не равно 'GET'
или в ['POST', 'PUT', 'DELETE']
.
-
ip
равно '127.0.0.1'
, или не равно '127.0.0.1'
.
-
usingTime
больше, чем 3500
, или больше или равно 1500
и меньше или равно 3500
.
-
status
равно 404
, или не равно 200
, или больше или равно 400
и меньше 500.
-
headers.user-agent
нравится /*chrome*/i
.
Здесь маршруты идут следующим образом:
-
/apis/classified/server/logs?path=/apis/classfied
-
/apis/classified/server/logs?path.regex=*classfied*
-
/apis/classified/server/logs?method.ne=GET
-
/apis/classified/server/logs?method=POST&method=PATCH&method=PUT
-
/apis/classified/server/logs?usingTime.gte=1500&usingTime.lte=2500
-
/apis/classified/server/logs?headers.user-agent.regex=*chrome*
Если вы используете express.js в качестве сервера, ваш req.query будет структурирован следующим образом:
-
{"path": "/apis/classfied"}
-
{"path": {"regex": "*classfied*"}}
-
{"method": "DELETE"}
-
{"method": ["GET","POST","PUT"]}
-
{"usingTime": {"gte": "1500","lte": "2500"}}
(lte: меньше или равно)
-
{"status": {"ne": "200"}}}
(ne: не равно)
-
{"path": {"regex": "*classfied*"}}
-
{"headers": {"user-agent": {"regex": "*chrome*", "flag": "i"}}}
И вы собираетесь использовать много if-else, чтобы составить свой метод запросов mongoose или, возможно, строку SQL.
Ответ 6
Я бы реализовал его как диапазон, и если обе стороны открыты, просто не заполняйте его.
GET /customers/16-
GET /customers/-716
При запросе всех клиентов не добавляйте все, просто оставьте пустым
GET /customers
Или, когда вам нужно - знаки в вашем номере/коде, используйте это:
GET /customers/16/
GET /customers//716
GET /customers/16/716
Вы можете избежать косой черты, если они являются частью числа.
Ответ 7
Некоторые стандарты на основе REST предлагают адекватные подходы к этой проблеме. Например, https://www.hl7.org/fhir/stu3/search.html
Ваша проблема может быть решена следующим образом:
GET /customers?id=ge500&id=lt1000
Кроме того, OData, который гораздо более совместим с любым стандартом на уровне отрасли. И он предлагает этот стиль: GET /customers?$filter=id ge 500 and id lt 1000
Ответ 8
ПРЕДУПРЕЖДЕНИЕ. Прочитайте отказ от ответственности ниже
Я не знаю, связан ли этот quetion с С#. Но если вы используете linq для своих запросов на стороне сервера, у меня есть другое предложение:
GET /customers?filter=CustomerNo>=16 AND CustomerNo<=716
Затем на стороне сервера:
using System.Linq.Dynamic;
var filteredCustomers = myCustomerTable.Where(filter);
Это должно быть самым гибким решением для всех, я думаю.
ПРЕДУПРЕЖДЕНИЕ добавлено 19 января 2017 г.
Я просто обратил внимание на то, что для Dynamic Linq существует эксплойт для инъекций кода. В приведенном выше примере открывается возможность того, что клиент может запустить выполнение кода на сервере.
Ответ 9
Вот как я это делал.
Допустим, у вас есть поле age, которое является числом.
Вот как будут выглядеть URL-адреса
Равно: /filter/age = 5
Больше чем: /filter/age [gt] = 5
Больше чем равно: /filter/age [gte] = 5
Меньше, чем: /filter/age [lt] = 5
Меньше чем равно: /filter/age [lte] = 5
Не равно: /filter/age [ne] = 5
Затем, когда я передаю эти аргументы бэкэнду, у меня есть скрипт, который просто анализирует ключ и преобразует его в правильный фильтр на основе возраста [INSERT_OPERATOR_HERE]
Ответ 10
Я бы взял
GET /customers/greaterthan=16;lessthan=716/
поскольку порядок не важен. Вы можете даже разбивать страницы сверху:
GET /customers/greaterthan=16;lessthan=716/page/10
В качестве альтернативы (с соответствующим маршрутизатором запроса):
GET /customers/16-716/page/10
И без "фильтра":
GET /customers/all/page/10