When I tried to build an application with axum, I failed to separate the framework from my handler. With Go, the classic way is define an Interface, implement it and register the handler to framework. In this way, it's easy to provide a mock handler to test with. However, I couldn't make it work with Axum. I defined a trait just like above, but it wouldn't compile:
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
}
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 = _`
I guess the key point is my design is apparently not "rustic". Is there a way to structure an Axum project that lends itself to testing easily?