axum crate の IntoResponse の標準で提供される実装 (2)
前回は axum crate の IntoResponse の標準で提供される実装を見ました。
具体的には次のようなものを見ました。
impl<B> IntoResponse for Response<B>impl IntoResponse for Bodyimpl IntoResponse for Partsimpl<R> IntoResponse for (Parts, R)
最後のものは特に T16 までの可変の IntoResponseParts と 1 つの IntoResponse を取る多くのパターンを含むものでした。
今回は前回に続き axum crate の IntoResponse trait の実装を見ていきます。
今回も axum crate のバージョンは 0.8.6 です。
impl<T, E> IntoResponse for Result<T, E>
T: IntoResponse, E: IntoResponse
https://docs.rs/axum/0.8.6/axum/response/trait.IntoResponse.html#impl-IntoResponse-for-Result<T,+E>
impl<T, E> IntoResponse for Result<T, E>
where
T: IntoResponse,
E: IntoResponse,
{
fn into_response(self) -> Response {
match self {
Ok(value) => value.into_response(),
Err(err) => err.into_response(),
}
}
}
Result を扱うものです。簡単な定義なんですけど、重要です。
これのおかげで E を IntoResponse にさえすれば独自のエラーでのハンドリングができます。
impl IntoResponse for PathRejection
https://docs.rs/axum/0.8.6/axum/response/trait.IntoResponse.html#impl-IntoResponse-for-PathRejection
実装を見る前にすこし説明を。
impl IntoResponse for PathRejection は Rejection を扱うものの例です。 Rejection という suffix のデータ型はいくつかあり、 Extractor のエラーに使用されています。
たとえば PathRejection は Path Extractor の impl<T, S> FromRequestParts<S> for Path<T> で FromRequestParts の関連型である type Rejection に使用されています。
- https://docs.rs/axum/0.8.6/axum/extract/rejection/enum.PathRejection.html
- https://docs.rs/axum/0.8.6/axum/extract/struct.Path.html
- https://docs.rs/axum/0.8.6/axum/extract/struct.Path.html#impl-FromRequestParts<S>-for-Path<T>
Extractor の失敗時に返されるエラー型ということです。
各 Extractor はそれぞれの Rejection を持っていることが多そうです。
そして実装を見ます。
composite_rejection! {
/// Rejection used for [`Path`](super::Path).
///
/// Contains one variant for each way the [`Path`](super::Path) extractor
/// can fail.
pub enum PathRejection {
FailedToDeserializePathParams,
MissingPathParams,
}
}
composite_rejection! は axum-core の↓で定義されています。
https://github.com/tokio-rs/axum/blob/axum-v0.8.6/axum-core/src/macros.rs#L154
macro_rules! __composite_rejection {
(
$(#[$m:meta])*
pub enum $name:ident {
$($variant:ident),+
$(,)?
}
) => {
$(#[$m])*
#[derive(Debug)]
#[non_exhaustive]
pub enum $name {
$(
#[allow(missing_docs)]
$variant($variant)
),+
}
impl $crate::response::IntoResponse for $name {
fn into_response(self) -> $crate::response::Response {
match self {
$(
Self::$variant(inner) => inner.into_response(),
)+
}
}
}
// ...
};
}
各 variant name と同一名の型に処理を転送するようなマクロです。いくつかのメソッドなどを転送しているのですが、ここでは IntoResponse だけあれば十分イメージできそうです。
PathRejection の FailedToDeserializePathParams なら↓の型に転送しています。
https://docs.rs/axum/0.8.6/axum/extract/path/struct.FailedToDeserializePathParams.html
同じような Rejection でも enum でなく struct の場合もあります。
NestedPath Extractor のための NestedPathRejection は struct です。これも impl<S> FromRequestParts<S> for NestedPath の type Rejection に使用されています。
- https://docs.rs/axum/0.8.6/axum/extract/struct.NestedPath.html
- https://docs.rs/axum/0.8.6/axum/extract/rejection/struct.NestedPathRejection.html
- https://docs.rs/axum/0.8.6/axum/extract/struct.NestedPath.html#impl-FromRequestParts<S>-for-NestedPath
実装は↓です。
define_rejection! {
#[status = INTERNAL_SERVER_ERROR]
#[body = "The matched route is not nested"]
/// Rejection type for [`NestedPath`](super::NestedPath).
///
/// This rejection is used if the matched route wasn't nested.
pub struct NestedPathRejection;
}
define_rejection は axum-core で定義されています。
macro_rules! __define_rejection {
(
#[status = $status:ident]
#[body = $body:literal]
$(#[$m:meta])*
pub struct $name:ident;
) => {
$(#[$m])*
#[derive(Debug)]
#[non_exhaustive]
pub struct $name;
// ...
impl $crate::response::IntoResponse for $name {
fn into_response(self) -> $crate::response::Response {
let status = self.status();
$crate::__log_rejection!(
rejection_type = $name,
body_text = $body,
status = status,
);
(status, $body).into_response()
}
}
// ...
};
(
#[status = $status:ident]
#[body = $body:literal]
$(#[$m:meta])*
pub struct $name:ident (Error);
) => {
// ...
};
}
こちらは field に何も持たない型や Error を持った型などを定義するものです。さきほどの macro と同様にいろいろな転送をしています。
個人的には macro_rules! にそこまで慣れていないので、こういうこともできるんだ……と勉強になります。
おわりに
今回は axum crate の IntoResponse の標準で提供される実装のうちエラーっぽいものや Result<T, E> について見ました。
次回は axum crate のエラーハンドリングについて見ます。
Discussion