Вернуть JSON с HTTP-статусом, отличным от 200, в Rocket
Я хочу, чтобы у моего Rocket API был такой маршрут:
#[post("create/thing", format = "application/json", data="<thing>")]
Когда клиент отправляет { "name": "mything" }
все должно быть хорошо, и я знаю, как это сделать, но когда он отправляет { "name": "foo" }
он должен ответить примерно так:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"errors": [
{
"status": "422",
"title": "Invalid thing name",
"detail": "The name for a thing must be at least 4 characters long."
}
]
}
Как мне ответить с результатом, как объект JSON и код состояния HTTP, отличный от 200 в Rocket?
Это то, что я пробовал до сих пор:
impl
FromRequest
для меняThing
тип. Это позволяет мне выбрать код состояния, так как я могу написать свой собственныйfrom_request
функции, но я не могу ничего больше вернуть.- Регистрация ловушки ошибок, как в этом примере, но таким образом я могу реагировать только на один код состояния HTTP без контекста. У меня слишком много режимов сбоя, чтобы зарезервировать один код состояния HTTP для каждого.
2 ответа
С помощью @hellow я понял это. Решение заключается в реализации Responder
черта для новой структуры ApiResponse
, который содержит код состояния, а также Json
, Таким образом, я могу делать именно то, что хотел:
#[post("/create/thing", format = "application/json", data = "<thing>")]
fn put(thing: Json<Thing>) -> ApiResponse {
let thing: Thing = thing.into_inner();
match thing.name.len() {
0...3 => ApiResponse {
json: json!({"error": {"short": "Invalid Name", "long": "A thing must have a name that is at least 3 characters long"}}),
status: Status::UnprocessableEntity,
},
_ => ApiResponse {
json: json!({"status": "success"}),
status: Status::Ok,
},
}
}
Вот полный код:
#![feature(proc_macro_hygiene)]
#![feature(decl_macro)]
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate rocket_contrib;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use rocket::http::{ContentType, Status};
use rocket::request::Request;
use rocket::response;
use rocket::response::{Responder, Response};
use rocket_contrib::json::{Json, JsonValue};
#[derive(Serialize, Deserialize, Debug)]
pub struct Thing {
pub name: String,
}
#[derive(Debug)]
struct ApiResponse {
json: JsonValue,
status: Status,
}
impl<'r> Responder<'r> for ApiResponse {
fn respond_to(self, req: &Request) -> response::Result<'r> {
Response::build_from(self.json.respond_to(&req).unwrap())
.status(self.status)
.header(ContentType::JSON)
.ok()
}
}
#[post("/create/thing", format = "application/json", data = "<thing>")]
fn put(thing: Json<Thing>) -> ApiResponse {
let thing: Thing = thing.into_inner();
match thing.name.len() {
0...3 => ApiResponse {
json: json!({"error": {"short": "Invalid Name", "long": "A thing must have a name that is at least 3 characters long"}}),
status: Status::UnprocessableEntity,
},
_ => ApiResponse {
json: json!({"status": "success"}),
status: Status::Ok,
},
}
}
fn main() {
rocket::ignite().mount("/", routes![put]).launch();
}
Вам нужно построить ответ. Посмотрите на ResponseBuilder
, Ваш ответ может выглядеть примерно так.
use std::io::Cursor;
use rocket::response::Response;
use rocket::http::{Status, ContentType};
let response = Response::build()
.status(Status::UnprocessableEntity)
.header(ContentType::Json)
.sized_body(Cursor::new("Your json body"))
.finalize();