Разработка API запросов RESTful с длинным списком параметров запросов
Итак, мне нужно разработать API запросов RESTful, который возвращает набор объектов на основе нескольких фильтров. Обычный метод HTTP для этого - GET. Единственная проблема в том, что он может иметь не менее дюжины фильтров, и если мы передадим все из них в качестве параметров запроса, URL-адрес может стать довольно длинным (достаточно длинным, чтобы быть заблокированным каким-либо брандмауэром).
Уменьшение количества параметров не вариант.
Одна альтернатива, о которой я мог подумать, - это использовать метод POST для URI и отправлять фильтры как часть тела POST. Это против того, чтобы быть RESTfull (Выполнение вызова POST для запроса данных).
У кого-нибудь есть лучшие дизайнерские предложения?
Спасибо
4 ответа
Помните, что с REST API все зависит от вашей точки зрения.
Двумя ключевыми понятиями в REST API являются конечные точки и ресурсы (сущности). Проще говоря, конечная точка либо возвращает ресурсы через GET, либо принимает ресурсы через POST и PUT и т. Д. (Или комбинацию вышеперечисленного).
Принято считать, что с помощью POST отправляемые вами данные могут или не могут привести к созданию нового ресурса и связанных с ним конечных точек, которые, скорее всего, не будут "жить" под URL-адресом POST. Другими словами, когда вы отправляете POST, вы отправляете данные куда-то для обработки. Конечная точка POST находится не там, где обычно может быть найден ресурс.
Цитата из RFC 2616 (опущены нерелевантные части и выделены соответствующие части):
9,5 ПОСТ
Метод POST используется для запроса, чтобы исходный сервер принял объект, включенный в запрос, в качестве нового подчиненного ресурса, идентифицируемого Request-URI в строке запроса. POST разработан для того, чтобы унифицированный метод мог выполнять следующие функции:
- ...
- Предоставление блока данных, такого как результат отправки формы, процессу обработки данных;
- ...
...
Действие, выполняемое методом POST, может не привести к ресурсу, который может быть идентифицирован с помощью URI. В этом случае либо 200 (ОК), либо 204 (Нет содержимого) является подходящим статусом ответа, в зависимости от того, включает ли ответ объект, который описывает результат.
Если ресурс был создан на исходном сервере, ответ ДОЛЖЕН быть 201 (Создан)...
Мы привыкли к конечным точкам и ресурсам, представляющим "вещи" или "данные", будь то пользователь, сообщение, книга - все, что диктует проблемная область. Однако конечная точка также может предоставлять другой ресурс - например, результаты поиска.
Рассмотрим следующий пример:
GET /books?author=AUTHOR
POST /books
PUT /books/ID
DELETE /books/ID
Это типичный REST CRUD. Однако что если мы добавим:
POST /books/search
{
"keywords": "...",
"yearRange": {"from": 1945, "to": 2003},
"genre": "..."
}
В этой конечной точке нет ничего плохого. Он принимает данные (сущность) в виде тела запроса. Эти данные являются критериями поиска - DTO, как и любой другой. Эта конечная точка создает ресурс (сущность) в ответ на запрос: Результаты поиска. Ресурс результатов поиска является временным ресурсом, который незамедлительно предоставляется клиенту, без перенаправления и без предоставления какого-либо другого канонического URL.
Это все еще REST, за исключением того, что объекты не являются книгами - объект запроса является критерием поиска книги, а объект ответа - результаты поиска книги.
Многие люди приняли практику, что GET со слишком длинной или слишком сложной строкой запроса (например, строки запроса не могут легко обрабатывать вложенные данные) можно вместо этого отправлять как POST, причем сложные / длинные данные представлены в теле запроса.
Посмотрите спецификацию для POST в спецификации HTTP. Это невероятно широко. (Если вы хотите проплыть линкор через лазейку в REST... используйте POST.)
Вы теряете некоторые преимущества семантики GET... например, автоматические повторы, потому что GET является идемпотентом, но если вы можете с этим смириться, может быть проще просто принять обработку действительно длинных или сложных запросов с помощью POST.
(Длинное отступление... Я недавно обнаружил, что по спецификации HTTP, GET может содержать тело документа. Есть один раздел, который говорит, перефразируя: "Любой запрос может иметь тело документа, кроме перечисленных в этом разделе"... и в разделе, на который он ссылается, ничего не перечислено. Я искал и нашел поток, где авторы HTTP говорили об этом, и это было сделано намеренно, так что маршрутизаторам и тому подобному не пришлось бы различать разные сообщения. попрактикуйтесь во многих элементах инфраструктуры, которые действительно отбрасывают тело GET. Таким образом, вы можете получить GET с фильтрами, представленными в теле, например, POST, но вы будете бросать кости.)
В двух словах: создайте POST, но переопределите HTTP-метод, используя заголовок X-HTTP-Method-Override.
Реальный запрос
ПОСТ / книги
Тело сущности
{ "title": "Ipsum", "year": 2017 }
Заголовки
X-HTTP-Method-Override: GET
На стороне сервера проверьте, существует ли заголовок X-HTTP-Method-Override, затем примите его значение в качестве метода для построения маршрута к конечной конечной точке в серверной части. Кроме того, возьмите тело объекта в качестве строки запроса. С внутренней стороны запрос стал просто GET.
Таким образом, вы сохраняете дизайн в гармонии с принципами REST.
Изменить: Я знаю, что это решение изначально предназначалось для решения проблемы глагола PATCH в некоторых браузерах и серверах, но оно также работает для меня с глаголом GET в случае очень длинного URL, который является проблемой, описанной в вопросе.
Если вы разрабатываете на Java и JAX-RS, я рекомендую вам использовать @QueryParam с @GET
У меня был тот же вопрос, когда мне нужно было просмотреть список.
Смотрите пример:
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
@Path("/poc")
public class UserService {
@GET
@Path("/test/")
@Produces(MediaType.APPLICATION_JSON)
public Response test(@QueryParam("code") final List<Integer> code) {
Integer int0 = codigo.get(0);
Integer int1 = codigo.get(1);
return Response.ok(new JSONObject().put("int01", int0)).build();
}
}
Шаблон URI: "poc/test? Code=1&code=2&code=3"
@QueryParam автоматически преобразует параметр запроса "orderBy=age&orderBy=name" в java.util.List.