Вернуть JSON с HTTP-статусом, отличным от 200, в Rocket - PullRequest
0 голосов
/ 25 февраля 2019

Я хочу, чтобы у моего 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 ]

0 голосов
/ 25 февраля 2019

С помощью @ 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();
}
0 голосов
/ 25 февраля 2019

Вам необходимо создать ответ.Взгляните на 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();
...