cloudwego / volo

Rust RPC framework with high-performance and strong-extensibility for building micro-services.
https://crates.io/crates/volo
Apache License 2.0
2.21k stars 181 forks source link

feat(volo-http): support any body or error for server #434

Closed yukiiiteru closed 4 months ago

yukiiiteru commented 4 months ago

This commit add more generic types for Service on server side. The body and error can be converted through layers, e.g., you can use the middleware::from_fn for convert Service<Cx, Request<Incoming>> to Service<Cx, Request<Bytes>>.

Example is also updated.

Motivation

In previous implementation, the Router and MethodRouter only supports fixed Body (http::Request with volo_http::body::Body), which is inconvenient.

Solution

This PR makes layer able to convert types of body and error. An example is updated:

async fn test_body_and_err(
    _: &mut ServerContext,
    _: ServerRequest<Bytes>,
) -> Result<ServerResponse, ()> {
    Ok(ServerResponse::default())
}

async fn map_body_and_err(
    cx: &mut ServerContext,
    req: ServerRequest,
    next: Next<Bytes, ()>,
) -> Result<ServerResponse, Infallible> {
    let (parts, body) = req.into_parts();
    let body = body.into_bytes().await.unwrap();
    let req = Request::from_parts(parts, body);
    Ok(next.run(cx, req).await.unwrap())
}

fn body_error_router() -> Router {
    Router::new()
        .route(
            "/body_err/test",
            get_service(service_fn(test_body_and_err))
                .post_service(service_fn(test_body_and_err)),
        )
        .layer(middleware::from_fn(map_body_and_err))
}

The outer Router uses Request with Body and Error = Infallible, while the inner service uses Request with Bytes and Error = (), and please notice that the middleware converted the types for supporting the operation.

Because all the above types have default generic types (body is hyper::body::Incoming and error is Infallible), in this PR, no break changes introduced. But if you want to use this feature, the FromRequest, Next (used for middleware::from_fn) should be updated with a new generic type B as type of the body.