How to use trait object or generics type as route handler with Axum? #358
-
Problem I metI'm beginner with axum, When tried to build an application with Axum, I could only share a handler What I wantWith Go, The classic way is define an DemoA demo is listed here: use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use serde_derive::{Serialize, Deserialize};
use serde_json::json;
use axum::{Server, Router, Json};
use axum::extract::Extension;
use axum::routing::BoxRoute;
use axum::handler::get;
#[tokio::main]
async fn main() {
let app = new_router(
Foo{}
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
trait Handler {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse{ message: "It works.".to_owned()})
}
}
fn new_router<T:Handler>(handler: T) -> Router<BoxRoute> {
Router::new()
.route("/", get(helper))
.boxed()
}
fn helper<T:Handler>(
Extension(mut handler): Extension<T>,
Json(req): Json<GetRequest>
) -> Json<GetResponse> {
Json(handler.get(req).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
struct GetRequest {
// omited
}
#[derive(Debug, Serialize, Deserialize)]
struct GetResponse {
message: String
// omited
} And the error reported by Rust is listed here. error[E0599]: the method `boxed` exists for struct `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>`, but its trait bounds were not satisfied
--> src/router.rs:25:10
|
25 | .boxed()
| ^^^^^ method cannot be called on `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>` due to unsatisfied trait bounds
|
::: /Users/lebrancebw/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.2.5/src/routing/mod.rs:876:1
|
876 | pub struct Layered<S> {
| --------------------- doesn't satisfy `<_ as tower_service::Service<Request<_>>>::Error = _`
|
= note: the following trait bounds were not satisfied:
`<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>> as tower_service::Service<Request<_>>>::Error = _`
error: aborting due to previous error; 13 warnings emitted
For more information about this error, try `rustc --explain E0599`.
error: could not compile `short_url`
To learn more, run the command again with --verbose. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Rust and Go have entirely different type systems so in general you can't just "copy" the Go approach and apply it to Rust. It looks to me like you're looking for a way to do dependency injection which there is an example for here. To actually get your code to compile you have to:
The final version is: use axum::extract::Extension;
use axum::handler::get;
use axum::routing::BoxRoute;
use axum::{Json, Router, Server};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
#[tokio::main]
async fn main() {
let app = new_router(Foo {});
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
// adding the traits as super traits here means you don't have to specify them
// in each `where` clause
trait Handler: Clone + Send + Sync + 'static {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
#[derive(Clone)]
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse {
message: "It works.".to_owned(),
})
}
}
fn new_router<T: Handler>(handler: T) -> Router<BoxRoute> {
Router::new().route("/", get(helper::<T>)).boxed()
}
async fn helper<T: Handler>(
Extension(mut handler): Extension<T>,
Json(req): Json<GetRequest>,
) -> Json<GetResponse> {
Json(handler.get(req).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
struct GetRequest {
// omited
}
#[derive(Debug, Serialize, Deserialize)]
struct GetResponse {
message: String, // omited
} |
Beta Was this translation helpful? Give feedback.
Rust and Go have entirely different type systems so in general you can't just "copy" the Go approach and apply it to Rust.
It looks to me like you're looking for a way to do dependency injection which there is an example for here.
To actually get your code to compile you have to:
fn helper
an async function. All axum handlers must be async. Soasync fn helper
impl FromRequest for Extension<T>
in the docs you can see it requiresT
to impl…