2016-09-19 23:24:01 +00:00
|
|
|
extern crate serde;
|
|
|
|
extern crate serde_json;
|
|
|
|
|
|
|
|
use std::ops::{Deref, DerefMut};
|
2016-10-12 07:14:42 +00:00
|
|
|
use std::io::Read;
|
2016-09-19 23:24:01 +00:00
|
|
|
|
2016-12-22 06:56:58 +00:00
|
|
|
use rocket::outcome::Outcome;
|
2016-10-25 11:24:07 +00:00
|
|
|
use rocket::request::Request;
|
|
|
|
use rocket::data::{self, Data, FromData};
|
2016-10-25 11:03:50 +00:00
|
|
|
use rocket::response::{self, Responder, content};
|
2016-12-15 08:47:31 +00:00
|
|
|
use rocket::http::Status;
|
2016-09-19 23:24:01 +00:00
|
|
|
|
|
|
|
use self::serde::{Serialize, Deserialize};
|
2017-01-13 07:07:01 +00:00
|
|
|
|
|
|
|
pub use self::serde_json::error::Error as SerdeError;
|
2016-09-19 23:24:01 +00:00
|
|
|
|
2016-12-29 03:01:16 +00:00
|
|
|
pub use self::serde_json::value::Value;
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub use self::serde_json::to_value;
|
|
|
|
|
2016-10-12 07:14:42 +00:00
|
|
|
/// The JSON type, which implements `FromData` and `Responder`. This type allows
|
|
|
|
/// you to trivially consume and respond with JSON in your Rocket application.
|
2016-09-19 23:24:01 +00:00
|
|
|
///
|
2016-10-12 07:14:42 +00:00
|
|
|
/// If you're receiving JSON data, simple add a `data` parameter to your route
|
|
|
|
/// arguments and ensure the type o the parameter is a `JSON<T>`, where `T` is
|
|
|
|
/// some type you'd like to parse from JSON. `T` must implement `Deserialize`
|
|
|
|
/// from [Serde](https://github.com/serde-rs/json). The data is parsed from the
|
|
|
|
/// HTTP request body.
|
2016-09-19 23:24:01 +00:00
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
2016-10-12 07:14:42 +00:00
|
|
|
/// #[post("/users/", format = "application/json", data = "<user>")]
|
2016-09-19 23:24:01 +00:00
|
|
|
/// fn new_user(user: JSON<User>) {
|
|
|
|
/// ...
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
/// You don't _need_ to use `format = "application/json"`, but it _may_ be what
|
|
|
|
/// you want. Using `format = application/json` means that any request that
|
2016-10-12 07:14:42 +00:00
|
|
|
/// doesn't specify "application/json" as its first `Content-Type:` header
|
|
|
|
/// parameter will not be routed to this handler.
|
2016-09-19 23:24:01 +00:00
|
|
|
///
|
|
|
|
/// If you're responding with JSON data, return a `JSON<T>` type, where `T`
|
2016-10-12 07:14:42 +00:00
|
|
|
/// implements `Serialize` from [Serde](https://github.com/serde-rs/json). The
|
|
|
|
/// content type of the response is set to `application/json` automatically.
|
2016-09-19 23:24:01 +00:00
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// #[get("/users/<id>")]
|
|
|
|
/// fn user(id: usize) -> JSON<User> {
|
|
|
|
/// let user_from_id = User::from(id);
|
|
|
|
/// ...
|
|
|
|
/// JSON(user_from_id)
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2016-09-22 11:12:07 +00:00
|
|
|
#[derive(Debug)]
|
2016-09-19 23:24:01 +00:00
|
|
|
pub struct JSON<T>(pub T);
|
|
|
|
|
|
|
|
impl<T> JSON<T> {
|
|
|
|
/// Consumes the JSON wrapper and returns the wrapped item.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// ```rust
|
|
|
|
/// # use rocket_contrib::JSON;
|
|
|
|
/// let string = "Hello".to_string();
|
|
|
|
/// let my_json = JSON(string);
|
2017-01-15 11:00:46 +00:00
|
|
|
/// assert_eq!(my_json.into_inner(), "Hello".to_string());
|
2016-09-19 23:24:01 +00:00
|
|
|
/// ```
|
2017-01-15 11:00:46 +00:00
|
|
|
pub fn into_inner(self) -> T {
|
2016-09-19 23:24:01 +00:00
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-12 07:14:42 +00:00
|
|
|
/// Maximum size of JSON is 1MB.
|
|
|
|
/// TODO: Determine this size from some configuration parameter.
|
|
|
|
const MAX_SIZE: u64 = 1048576;
|
|
|
|
|
|
|
|
impl<T: Deserialize> FromData for JSON<T> {
|
|
|
|
type Error = SerdeError;
|
|
|
|
|
2016-10-25 11:03:50 +00:00
|
|
|
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, SerdeError> {
|
2016-10-12 07:14:42 +00:00
|
|
|
if !request.content_type().is_json() {
|
|
|
|
error_!("Content-Type is not JSON.");
|
2016-10-25 11:03:50 +00:00
|
|
|
return Outcome::Forward(data);
|
2016-10-12 07:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let reader = data.open().take(MAX_SIZE);
|
2016-12-22 06:56:58 +00:00
|
|
|
match serde_json::from_reader(reader).map(|val| JSON(val)) {
|
|
|
|
Ok(value) => Outcome::Success(value),
|
|
|
|
Err(e) => {
|
|
|
|
error_!("Couldn't parse JSON body: {:?}", e);
|
|
|
|
Outcome::Failure((Status::BadRequest, e))
|
|
|
|
}
|
|
|
|
}
|
2016-09-19 23:24:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 10:38:30 +00:00
|
|
|
// Serializes the wrapped value into JSON. Returns a response with Content-Type
|
|
|
|
// JSON and a fixed-size body with the serialization. If serialization fails, an
|
|
|
|
// `Err` of `Status::InternalServerError` is returned.
|
2016-12-15 08:47:31 +00:00
|
|
|
impl<T: Serialize> Responder<'static> for JSON<T> {
|
|
|
|
fn respond(self) -> response::Result<'static> {
|
|
|
|
serde_json::to_string(&self.0).map(|string| {
|
|
|
|
content::JSON(string).respond().unwrap()
|
|
|
|
}).map_err(|e| {
|
|
|
|
error_!("JSON failed to serialize: {:?}", e);
|
2016-12-23 10:38:30 +00:00
|
|
|
Status::InternalServerError
|
2016-12-15 08:47:31 +00:00
|
|
|
})
|
2016-09-19 23:24:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Deref for JSON<T> {
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
fn deref<'a>(&'a self) -> &'a T {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> DerefMut for JSON<T> {
|
|
|
|
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-29 03:01:16 +00:00
|
|
|
/// A nice little macro to create JSON serializable HashMaps, convenient for
|
2016-09-19 23:24:01 +00:00
|
|
|
/// returning ad-hoc JSON messages.
|
|
|
|
///
|
2016-12-29 03:01:16 +00:00
|
|
|
/// Keys can be any type that implements `Serialize`. All keys must have the
|
|
|
|
/// same type, which is usually an `&'static str`. Values can be any type that
|
|
|
|
/// implements `Serialize` as well, but each value is allowed to be a different
|
|
|
|
/// type.
|
|
|
|
///
|
2016-09-19 23:24:01 +00:00
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # #[macro_use] extern crate rocket_contrib;
|
|
|
|
/// # fn main() {
|
2016-12-29 03:01:16 +00:00
|
|
|
/// let map = map! {
|
|
|
|
/// "message" => "Done!",
|
|
|
|
/// "success" => true,
|
|
|
|
/// "count" => 3,
|
2016-09-19 23:24:01 +00:00
|
|
|
/// };
|
|
|
|
///
|
2016-12-29 03:01:16 +00:00
|
|
|
/// assert_eq!(map.len(), 3);
|
|
|
|
/// assert_eq!(map.get("message").and_then(|v| v.as_str()), Some("Done!"));
|
|
|
|
/// assert_eq!(map.get("success").and_then(|v| v.as_bool()), Some(true));
|
|
|
|
/// assert_eq!(map.get("count").and_then(|v| v.as_u64()), Some(3));
|
2016-09-19 23:24:01 +00:00
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! map {
|
|
|
|
($($key:expr => $value:expr),+) => ({
|
2016-12-29 03:01:16 +00:00
|
|
|
use ::std::collections::HashMap;
|
|
|
|
use $crate::json::{Value, to_value};
|
|
|
|
let mut map: HashMap<_, Value> = HashMap::new();
|
|
|
|
$(map.insert($key, to_value($value));)+
|
2016-09-19 23:24:01 +00:00
|
|
|
map
|
|
|
|
});
|
|
|
|
|
|
|
|
($($key:expr => $value:expr),+,) => {
|
|
|
|
map!($($key => $value),+)
|
|
|
|
};
|
|
|
|
}
|