Return JSON 在 Rocket 中的 HTTP 状态不是 200
Return JSON with an HTTP status other than 200 in 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."
}
]
}
如何在 Rocket 中响应 JSON 对象和不同于 200 的 HTTP 状态代码?
这是我到目前为止尝试过的:
impl
FromRequest
适合我的 Thing
类型。这让我可以选择一个状态代码,因为我可以编写自己的 from_request
函数,但我不能 return 其他任何东西。
- 像 this example 一样注册一个错误捕捉器,但这样我只能在没有上下文的情况下对一个 HTTP 状态代码做出反应。我有太多的失败模式,无法为每个模式保留一个 HTTP 状态代码。
您需要建立回应。看看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();
在@hellow 的帮助下,我弄明白了。解决方案是为新结构 ApiResponse
实现 Responder
特征,它包含状态代码以及 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();
}
我希望我的火箭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."
}
]
}
如何在 Rocket 中响应 JSON 对象和不同于 200 的 HTTP 状态代码?
这是我到目前为止尝试过的:
impl
FromRequest
适合我的Thing
类型。这让我可以选择一个状态代码,因为我可以编写自己的from_request
函数,但我不能 return 其他任何东西。- 像 this example 一样注册一个错误捕捉器,但这样我只能在没有上下文的情况下对一个 HTTP 状态代码做出反应。我有太多的失败模式,无法为每个模式保留一个 HTTP 状态代码。
您需要建立回应。看看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();
在@hellow 的帮助下,我弄明白了。解决方案是为新结构 ApiResponse
实现 Responder
特征,它包含状态代码以及 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();
}