-
Notifications
You must be signed in to change notification settings - Fork 197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Utoipa users poll: How do you use responses and how would you like them to work? Answer would help. #1068
Comments
Hi, first of all huge thank you for this crate! To answer your questions. I use dedicated structs and deriving from I find it quite nice and I would not change it. There are two aspects I think would be nice if improved but I am not sure if possible:
|
Great, thanks for a reply.
|
Hey, not really an answer on how I uses it, but I would really like to be able to share responses between routes. For example, in Meilisearch, most routes will return something we call a Going from: /// Get a task
///
/// Get a [task](https://www.meilisearch.com/docs/learn/async/asynchronous_operations)
#[utoipa::path(
post,
path = "/{taskUid}",
tag = "Tasks",
params(("taskUid", format = UInt32, example = 0, description = "The task identifier", nullable = false)),
responses(
(status = 200, description = "Task successfully enqueued", body = SummarizedTaskView, content_type = "application/json", example = json!(
{
"uid": 1,
"indexUid": "movies",
"status": "succeeded",
"type": "documentAdditionOrUpdate",
"canceledBy": null,
"details": {
"receivedDocuments": 79000,
"indexedDocuments": 79000
},
"error": null,
"duration": "PT1S",
"enqueuedAt": "2021-01-01T09:39:00.000000Z",
"startedAt": "2021-01-01T09:39:01.000000Z",
"finishedAt": "2021-01-01T09:39:02.000000Z"
}
))
)
)]
async fn get_task(
... To: /// Get a task
///
/// Get a [task](https://www.meilisearch.com/docs/learn/async/asynchronous_operations)
#[utoipa::path(
post,
path = "/{taskUid}",
tag = "Tasks",
params(("taskUid", format = UInt32, example = 0, description = "The task identifier", nullable = false)),
responses(
(status = 200, description = "Task successfully enqueued", body = SummarizedTaskView, content_type = "application/json", example = task_succeeded())
)
)]
async fn get_task(
... I’m using the latest beta version, let me know if I missed something. |
Responses can be shared if used with struct MyResponse;
impl<'s> ToResponse<'s> for MyResponse {
fn response() -> (&'s str, Response) -> {
// add all the examples here programmatically
("MyResponse", Response::builder().build())
}
}
#[utoipa::path(
post,
path = "/{taskUid}",
tag = "Tasks",
params(("taskUid", format = UInt32, example = 0, description = "The task identifier", nullable = false)),
responses(
(status = 200, response = inline(MyResponse))
)
)]
async fn get_task(} {} You could also derive the
|
Humm, yes, I tried to use it, but it didn’t work, and I don’t really have the time to understand why, It's probably an issue on my side. But in terms of API, I find it strange that the Ideally, in our case, almost every route can have three different errors that share the same error code: If you forget to specify an API key, we don’t understand the format of your API key, or your API key doesn’t give you the right to access this route. #[utoipa::path(
post,
path = "/{taskUid}",
tag = "Tasks",
params(("taskUid", format = UInt32, example = 0, description = "The task identifier", nullable = false)),
responses(
inline(ApiKeyError), // here I can define multiple examples with multiple HTTP status code
inline(SummarizedTaskView), // here I only have one example
(status = BAD_REQUEST, schema = ResponseError, example = json!(...)),
)
)]
async fn get_task(} {} I hope that can help you in your decisions! |
@irevoire Thanks for the response. Yeah that is worth thinking about. The current
|
Spend way too much time wrtiting this 😆 My reasoning/ViewpointFor us usually following is true:
Why:
However: There are some rare cases where I want percise control over the errors, I would be fine having a a litte more clunky api for these cases. At the moment, we usually do the tuple based approach with impl Responder as the return - so e.g. #[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct Task {
...
}
/// Retrieves a certain task by id
#[utoipa::path(
responses(
(status = 200, body = dto::Task),
)
)]
#[get("/tasks/{id}")]
pub async fn get_task_by_id(id : web::Path<i32>) -> impl Responder {
println!("Should now get a task with id {}", id.into_inner());
Ok(Json(Task:rand()))
}
My dream would be: #[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema, IntoResponses)]
#[response(status = default_ok)] // Maybe allow this to be optional ? No defined = default ok - So Get 200, Post 201...
pub struct Task {
...
}
/// Retrieves a certain task by id
#[utoipa::path]
#[get("/tasks/{id}")]
pub async fn get_task_by_id(id : web::Path<i32>) -> Result<Json<dto::Task>> {
println!("Should now get a task with id {}", id.into_inner());
Ok(Json(Task:rand()))
} What I would love to see:
How I might start using this nowI might implement my own Result type like: pub enum GetResult<Ok> {
Ok(actix_web::web::Json<Ok>),
Err(actix_web::Error),
}
impl<Ok:Serialize> Responder for GetResult<Ok> {
type Body = EitherBody<<actix_web::web::Json<Ok> as Responder>::Body>;
fn respond_to(self, req: &actix_web::HttpRequest) -> actix_web::HttpResponse<Self::Body> {
match self {
GetResult::Ok(t) => t.respond_to(req).map_into_left_body(),
GetResult::Err(e) => e.error_response().map_into_right_body(),
}
}
}
impl<Ok: for <'a> ToResponse<'a>> IntoResponses for GetResult<Ok> {
fn responses() -> std::collections::BTreeMap<String, utoipa::openapi::RefOr<utoipa::openapi::response::Response>> {
let mut map = std::collections::BTreeMap::new();
map.insert("200".to_string(), Ok::response().1);
map
}
}
/// ... PostResult Don't love it as I'll have to into() all my responses this way, but not sure if there is a different idea. Or possibly might add some post processing to the generated OpenAPI |
Responses poll
Currently while I am working on refactoring multiple aspects of the
utoipa
lib for the coming release 5.0.0 this is good place to introduce some improvements to current workflow within boundaries set by its users. So here comes the question.How do you currently use the responses with
utoipa
lib?ToResponse
Second question is how would you like the responses work, or is there any aspect you would like to change in order to them to be more user friendly?
The text was updated successfully, but these errors were encountered: