Closed5

Webアプリ開発で学ぶRust言語入門やる

shuntakashuntaka

このトレイト境界が難しい

#[async_trait]
impl<T, B> FromRequest<B> for ValidatedJson<T>
where
    T: DeserializeOwned + Validate,
    B: http_body::Body + Send, // NOTE: 最新バージョンだとコンパイルエラー
    B::Data: Send,
    B::Error: Into<BoxError>,
{
    type Rejection = (StatusCode, String);

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
        let Json(value) = Json::<T>::from_request(req).await.map_err(|rejection| {
            let message = format!("Json parse error: [{}]", rejection);

            (StatusCode::BAD_REQUEST, message)
        })?;
        value.validate().map_err(|rejection| {
            let message = format!("Validation error: [{}]", rejection).replace('\n', ", ");
            (StatusCode::BAD_REQUEST, message)
        })?;

        Ok(ValidatedJson(value))
    }
}

shuntakashuntaka

どう考えれば導出できるか考えてみる

```rust
#[async_trait]
pub trait FromRequest<B>: Sized {
    /// If the extractor fails it'll use this "rejection" type. A rejection is
    /// a kind of error that can be converted into a response.
    type Rejection: IntoResponse;

    /// Perform the extraction.
    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection>;
}

shuntakashuntaka

DeserializeOwned はコピーが走っているのかな。Deserializeはjsonの参照を持っていてライフタイムの制約があるから、使えないのかな。

use serde::{Deserialize, Serialize};
use std::borrow::Cow;

#[derive(Serialize, Deserialize)]
struct NormalStruct {
    data: String,
}

#[derive(Serialize, Deserialize)]
struct BorrowingStruct<'a> {
    data: Cow<'a, str>,
}

fn deserialize_owned<T: serde::de::DeserializeOwned>(json: &str) -> Result<T, serde_json::Error> {
    serde_json::from_str(json)
}

fn deserialize_lifetime<'a, T: Deserialize<'a>>(json: &'a str) -> Result<T, serde_json::Error> {
    serde_json::from_str(json)
}

fn main() {
    let json = r#"{"data": "test"}"#;

    // DeserializeOwned (works with owned data)
    let normal: NormalStruct = deserialize_owned(json).unwrap();
    println!("Normal: {}", normal.data);

    // Deserialize (can work with borrowed data)
    let borrowing: BorrowingStruct = deserialize_lifetime(json).unwrap();
    println!("Borrowing: {}", borrowing.data);

    // Serialize (for completeness)
    let serialized = serde_json::to_string(&normal).unwrap();
    println!("Serialized: {}", serialized);
}

shuntakashuntaka

Json<T>::from_request(req)を実装するんだから、定義通りトレイト境界は書けばいいんだな

#[async_trait]
impl<T, B> FromRequest<B> for Json<T>
where
    T: DeserializeOwned,
    B: HttpBody + Send,
    B::Data: Send,
    B::Error: Into<BoxError>,
{
    type Rejection = JsonRejection;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
        if json_content_type(req)? {
            let bytes = Bytes::from_request(req).await?;

            let value = serde_json::from_slice(&bytes).map_err(InvalidJsonBody::from_err)?;

            Ok(Json(value))
        } else {
            Err(MissingJsonContentType.into())
        }
    }
}

このスクラップは2024/12/27にクローズされました