📝

axum crate の IntoResponse の標準で提供される実装 (2)

に公開

前回は axum crate の IntoResponse の標準で提供される実装を見ました

具体的には次のようなものを見ました。

  • impl<B> IntoResponse for Response<B>
  • impl IntoResponse for Body
  • impl IntoResponse for Parts
  • impl<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 を扱うものです。簡単な定義なんですけど、重要です。

これのおかげで EIntoResponse にさえすれば独自のエラーでのハンドリングができます。

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 のエラーに使用されています。

たとえば PathRejectionPath Extractor の impl<T, S> FromRequestParts<S> for Path<T>FromRequestParts の関連型である type Rejection に使用されています。

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 だけあれば十分イメージできそうです。

PathRejectionFailedToDeserializePathParams なら↓の型に転送しています。

https://docs.rs/axum/0.8.6/axum/extract/path/struct.FailedToDeserializePathParams.html

同じような Rejection でも enum でなく struct の場合もあります。

NestedPath Extractor のための NestedPathRejectionstruct です。これも impl<S> FromRequestParts<S> for NestedPathtype Rejection に使用されています。

実装は↓です。

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 のエラーハンドリングについて見ます。

GitHubで編集を提案
ドクターメイト

Discussion