I am trying to write a custom extractor which will also validate the JSON body with validator
crate. To validate some of the fields I need custom function with args. Here is my code -
#[tokio::main]
async fn main() {
println!("Starting...");
dotenv().ok();
let uri = std::env::var("MONGODB_URI").expect("MONGODB_URI not found in .env file");
let min_pool = std::env::var("MONGODB_MIN_POOL_SIZE").unwrap_or_default();
let max_pool = std::env::var("MONGODB_MAX_POOL_SIZE").unwrap_or_default();
let min_pool = min_pool.parse::<u32>().unwrap_or(5);
let max_pool = max_pool.parse::<u32>().unwrap_or(10);
let timeout = Duration::from_secs(5);
// create the mongodb client options
let mut client_options = ClientOptions::parse(uri).await.unwrap();
client_options.max_pool_size = Some(max_pool);
client_options.min_pool_size = Some(min_pool);
client_options.connect_timeout = Some(timeout);
// create the client
let db_client = Client::with_options(client_options).unwrap();
let app = Router::new()
.route("/", get(handler).post(post_handler))
.with_state(db_client);
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
pub struct ValidatedBody<T>(pub T);
#[async_trait]
impl<'a, S, B, T> FromRequest<S, B> for ValidatedBody<T>
where
B: Send + 'static,
S: Send + Sync,
T: ValidateArgs<'a> + 'static,
Json<T>: FromRequest<(), B>,
{
type Rejection = (StatusCode, &'static str);
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
let Json(data) = req
.extract::<Json<T>, _>()
.await
.map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;
data.validate_args(state)
.map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;
Ok(Self(data))
}
}
async fn handler() -> &'static str {
"Hello from server"
}
#[derive(Debug, Serialize, Deserialize, Validate)]
struct MyReqBody {
#[validate(custom(function = "validate_name", arg = "&'v_a Client"))]
name: String,
}
fn validate_name(name: &str, db_client: &Client) -> Result<(), ValidationError> {
Ok(())
}
async fn post_handler(
State(client): State<Client>,
ValidatedBody(req_body): ValidatedBody<MyReqBody>,
) -> impl IntoResponse {
println!("{:?}", req_body);
(StatusCode::OK, Json(serde_json::json!({"success": true})))
}
The problem is that inside from_request
function I am not able to call data.validate_args
with one argument.
This is the error I am getting -
error[E0308]: mismatched types
--> src/main.rs:60:28
|
60 | data.validate_args(state)
| ------------- ^^^^^ expected associated type, found `&S`
| |
| arguments to this function are incorrect
|
= note: expected associated type `<T as ValidateArgs<'a>>::Args`
found reference `&'life0 S`
= help: consider constraining the associated type `<T as ValidateArgs<'a>>::Args` to `&'life0 S`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: associated function defined here
--> /Users/nebuchadnezzar/.cargo/registry/src/github.com-1ecc6299db9ec823/validator-0.16.0/src/traits.rs:211:8
|
211 | fn validate_args(&self, args: Self::Args) -> Result<(), ValidationErrors>;
| ^^^^^^^^^^^^^
Is there any way I can achieve this?