Handler — это ключевое понятие фреймворка Salvo, которое можно рассматривать как единицу обработки запросов. Он имеет два основных назначения:
Как конечная точка (Endpoint): Объекты, реализующие Handler
, могут быть размещены в системе маршрутизации в качестве конечных обработчиков запросов. При использовании макроса #[handler]
функция может напрямую выступать в роли конечной точки. Макрос #[endpoint]
не только позволяет создавать конечные точки, но и автоматически генерирует документацию OpenAPI (эта возможность будет подробно рассмотрена в последующих материалах).
Как промежуточное ПО (Middleware): Тот же Handler
может использоваться в качестве промежуточного слоя для обработки запроса до или после его достижения конечной точки.
Процесс обработки запросов в Salvo можно представить как "конвейер": запрос сначала проходит через серию промежуточных слоёв (вертикальная обработка), затем достигает соответствующей конечной точки (горизонтальная обработка). И промежуточные слои, и конечные точки являются реализациями Handler
, что обеспечивает согласованность и гибкость всей системы.
Суть модели луковицы заключается в том, что позиционирование ctrl.call_next()
до и после основного кода позволяет реализовать двунаправленный процесс обработки запросов и ответов, давая каждому промежуточному слою возможность участвовать в полном цикле "запрос-ответ".
Handler — это объект, отвечающий за обработку Request-запросов. Сам Handler является трейтом, содержащим асинхронный метод handle
:
Функция обработки handle
по умолчанию принимает четыре параметра: &mut Request, &mut Depot. &mut Response, &mut FlowCtrl
. Depot — это временное хранилище, которое может содержать данные, связанные с текущим запросом.
В зависимости от способа использования, Handler может выступать в качестве промежуточного слоя (hoop), выполняя обработку до или после основного обработчика запросов Handler
. Например: проверка авторизации, сжатие данных и т.д.
Промежуточные слои добавляются через функцию hoop
маршрутизатора Router
. Добавленные промежуточные слои влияют на текущий Router
и все его дочерние Router
.
Handler
также может использоваться как конечный обработчик Handler
, участвующий в сопоставлении маршрутов и выполняющийся в конце, называемый goal
.
Handler
как промежуточный слой (hoop)Когда Handler
используется как промежуточный слой, он может быть добавлен к следующим трём типам объектов, поддерживающих промежуточные слои:
Service
: все запросы проходят через промежуточные слои Service
.
Router
: только при успешном сопоставлении маршрута запрос последовательно проходит через промежуточные слои, определённые в Service
, и все промежуточные слои, собранные по соответствующему пути.
Catcher
: при возникновении ошибки и отсутствии пользовательской информации об ошибке запрос проходит через промежуточные слои Catcher
.
Handler
: сам Handler
поддерживает добавление промежуточных слоёв-обёрток для выполнения предварительной или последующей логики.
#[handler]
#[handler]
может значительно упростить написание кода и повысить его гибкость.
Он может быть применён к функции, заставляя её реализовать Handler
:
Это эквивалентно:
Как видно, при использовании #[handler]
код становится намного проще:
#[async_trait]
.Writer
или Scribe
, могут напрямую возвращаться из функции. В данном случае &'static str
реализует Scribe
, поэтому может быть возвращён напрямую.#[handler]
может применяться не только к функциям, но и к блоку impl
для struct
, заставляя struct
реализовать Handler
. При этом функция handle
в блоке impl
будет распознана как конкретная реализация handle
из Handler
:
Handler
в Salvo может возвращать Result
, при условии что типы Ok
и Err
в Result
реализуют трейт Writer
.
Учитывая широкое использование anyhow, при включении функции anyhow
, anyhow::Error
будет реализовывать трейт Writer
. anyhow::Error
будет преобразован в InternalServerError
.
Для пользовательских типов ошибок вы можете выводить различные страницы ошибок по необходимости.