Comments 6
Для сложных поисков я бы делал общий для всех сущностей endpoint /api/search
, и работал бы с ним по схеме POST/GET:
на URL
/api/search
отправляемPOST
со сложным запросом (возможно включающем в себя тип сущности);сервер сохраняет запрос в каком-то хранилище под уникальным идентификатором
id
;сервер в качестве результата
POST
отправляет клиентуid
;клиент отправляет
GET
на/api/search/{id}
;сервер получает по
id
параметры ранее сохраненного запроса из хранилища, выполняет его и отправляет клиенту результат этого выполнения;
Несмотря на дополнительные накладные расходы (два HTTP-запроса вместо одного и работа с хранилищем) такой подход дает много преимуществ, например, кеширование поиска, история запросов, возможность последующего анализа и т.д., плюс хорошо укладывается в идеологию ресурсов - в данном случае:
"поисковой запрос" это сам отдельный ресурс
/api/search/
- контейнер этого ресурса/api/search/{id}
- URI отдельного ресурсаPOST /api/search/
- глагол "создать ресурс"GET /api/search/{id}
- глагол "получить представление ресурса c идентификатором {id}".
Этот паттерн я разбирал в главе «Асинхронность и управление временем»
https://habr.com/ru/articles/732646/
Да, он вполне возможен с точки зрения архитектуры (хотя не совсем REST-way с той точки зрения, что id — черный ящик, по нему невозможно понять, что это была за операция).
Ну почему же "черный ящик". Вполне прозрачно: /container/subcontainer/susbsubcontainer/{id}
- id это идентификатор ресурса внутри контейнера /container/subcontainer/susbsubcontainer/
, соответственно полный path это полный идентификатор ресурса. А операция над ним (глагол) определяется по HTTP verb (кстати в английском "verb" это как раз и означает "глагол"): GET, PUT, DELETE.
В общем-то, как вы совершенно правильно написали, на REST нет какого-то официального стандарта, но, есть т.н. "REST maturity model" описанный, например в "REST in Practice" (отличная, кстати, книга) - согласно нему "все системы, по сути, REST, но некоторые более REST чем другие" :)
Я, в общем-то, адепт такого подхода, когда полный path это всегда какой-то (уникальный) ресурс (или контейнер ресурсов, впрочем, контейнер ведь это тоже частный случай ресурса), HTTP-verb это операция над ресурсом, а query string (если он есть) это какие либо "аспекты" этой операции (например, выбор того какое представление ресурса вернуть).
Только сейчас заметил комментарий ¯\_(ツ)_/¯
Этот подход, на самом деле, описывает такой чуть более REST-овый GraphQL: есть один эндпойнт, который умеет всё (только, отличие от GraphQL, с кэшированием по id). Проблемы те же:
Нельзя понять, что за операция была запрошена (надо тело читать). Отсюда такой сервис сложно профилировать (какие конкретно операции «тяжёлые») и ещё сложнее поставить рейтлимитер
Содержимое
GET /api/search/{id}
нетипизировано — невозможно узнать, что находится внутри (какие данные каких типов), пока не прочитаешь.
Неплохо было бы еще написать статью по "правильному" использованию в REST "HTTP status code". Потому что с этим повсюду совсем беда-беда. Чего только не насмотришься - видел как вообще на все что угодно возвращали "200", в случае ошибки возвращая её при этом в теле ответа, или наоборот на любую ошибку возвращали исключительно "500".
[HTTP API & REST] Разработка номенклатуры URL ресурсов. CRUD-операции