Not necessarily, I just wanted to understand your program a bit better. That is where I've ended up in my own code, but that's due to factors that might not translate to yours.
I'm not quite sure what you mean by your initial question?
The point of an Axum
handler method's return value is to generate an HTTP response, including its status code, headers, and body. The rust representation of the response is your application's means to that single end. If you are returning (status, None)
from a handler method, what HTTP response do you expect your service to send to the client?
The reason I ask is that Axum can only route to functions that return IntoResponse
types. Axum has a fairly large collection of implementations, including an implementation of IntoResponse
for tuple types of the form (T1, T2)
, so long as T1
and T2
both implement IntoResponse
themselves so long as T2
implements IntoResponse
and T1
implements IntoResponseParts
. However, there is no implementation of IntoResponse
for Option
.
In order for your program to work, you'll need to decide how you want your Option<Json<Vehicle>>
to be represented as an HTTP response, and then implement that. For coherence reasons, you'll likely have to wrap the whole mess in a new type, and at that point it might be easier to define your own option-like response type, instead.
Putting that all together gives this:
use axum::{
Json,
http::StatusCode,
response::{IntoResponse, Response},
};
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Vehicle {
manufacturer: String,
model: String,
year: u32,
id: Option<String>,
}
async fn item_get() -> (StatusCode, JsonOption<Vehicle>) {
(StatusCode::OK, JsonOption::None)
}
enum JsonOption<T> {
Some(T),
None,
}
impl<T> IntoResponse for JsonOption<T>
where
Json<T>: IntoResponse,
{
fn into_response(self) -> Response {
match self {
Self::Some(val) => Json(val).into_response(),
Self::None => StatusCode::NO_CONTENT.into_response(),
}
}
}
You can also pull the logic into your handler function, if you prefer. Response
implements IntoResponse
trivially, so you can write handlers like this:
async fn item_get() -> Response {
let vehicle: Option<Vehicle> = todo!();
match vehicle {
Some(v) => (StatusCode::OK, Json(vehicle)).into_response(),
None => StatusCode::NOT_FOUND.into_response(),
}
}
I don't do this because it makes it harder for tests to inspect application-specific characteristics of the response, but it works just fine and if you're not testing handlers directly you might not be concerned about that.