Replace 'SerdeError' with 'JsonError' in 'Json' data guard.

The new 'JsonError' type allows users to inspect the raw string that
failed to parse as a given JSON value.

Resolves #772.
This commit is contained in:
Sergio Benitez 2018-09-18 18:30:25 -07:00
parent 1a3fd1d237
commit 74007815a0
2 changed files with 33 additions and 25 deletions

View File

@ -1,5 +1,5 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::io::Read; use std::io::{self, Read};
use rocket::outcome::{Outcome, IntoOutcome}; use rocket::outcome::{Outcome, IntoOutcome};
use rocket::request::Request; use rocket::request::Request;
@ -11,24 +11,6 @@ use serde::{Serialize, Serializer};
use serde::de::{Deserialize, DeserializeOwned, Deserializer}; use serde::de::{Deserialize, DeserializeOwned, Deserializer};
use serde_json; use serde_json;
pub use serde_json::error::Error as SerdeError;
/// Like [`from_reader`] but eagerly reads the content of the reader to a string
/// and delegates to `from_str`.
///
/// [`from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
fn from_reader_eager<R, T>(mut reader: R) -> serde_json::Result<T>
where R: Read, T: DeserializeOwned
{
let mut s = String::with_capacity(512);
if let Err(io_err) = reader.read_to_string(&mut s) {
// Error::io is private to serde_json. Do not use outside of Rocket.
return Err(SerdeError::io(io_err));
}
serde_json::from_str(&s)
}
/// The JSON type: implements `FromData` and `Responder`, allowing you to easily /// The JSON type: implements `FromData` and `Responder`, allowing you to easily
/// consume and respond with JSON. /// consume and respond with JSON.
/// ///
@ -99,13 +81,39 @@ impl<T> Json<T> {
} }
} }
/// Like [`from_reader`] but eagerly reads the content of the reader to a string
/// and delegates to `from_str`.
///
/// [`from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
fn from_reader_eager<R, T>(mut reader: R) -> Result<T, JsonError>
where R: Read, T: DeserializeOwned
{
let mut s = String::with_capacity(512);
reader.read_to_string(&mut s).map_err(JsonError::Io)?;
serde_json::from_str(&s).map_err(|e| JsonError::Parse(s, e))
}
/// Default limit for JSON is 1MB. /// Default limit for JSON is 1MB.
const LIMIT: u64 = 1 << 20; const LIMIT: u64 = 1 << 20;
impl<T: DeserializeOwned> FromData for Json<T> { /// An error returned by the [`Json`] data guard when incoming data fails to
type Error = SerdeError; /// serialize as JSON.
#[derive(Debug)]
pub enum JsonError {
/// An I/O error occurred while reading the incoming request data.
Io(io::Error),
/// The client's data was received successfully but failed to parse as valid
/// JSON or as the requested type. The `String` value in `.0` is the raw
/// data received from the user, while the `Error` in `.1` is the
/// deserialization error from `serde`.
Parse(String, serde_json::error::Error),
}
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, SerdeError> { impl<T: DeserializeOwned> FromData for Json<T> {
type Error = JsonError;
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
if !request.content_type().map_or(false, |ct| ct.is_json()) { if !request.content_type().map_or(false, |ct| ct.is_json()) {
error_!("Content-Type is not JSON."); error_!("Content-Type is not JSON.");
return Outcome::Forward(data); return Outcome::Forward(data);
@ -122,8 +130,8 @@ impl<T: DeserializeOwned> FromData for Json<T> {
/// Serializes the wrapped value into JSON. Returns a response with Content-Type /// Serializes the wrapped value into JSON. Returns a response with Content-Type
/// JSON and a fixed-size body with the serialized value. If serialization /// JSON and a fixed-size body with the serialized value. If serialization
/// fails, an `Err` of `Status::InternalServerError` is returned. /// fails, an `Err` of `Status::InternalServerError` is returned.
impl<T: Serialize> Responder<'static> for Json<T> { impl<'a, T: Serialize> Responder<'a> for Json<T> {
fn respond_to(self, req: &Request) -> response::Result<'static> { fn respond_to(self, req: &Request) -> response::Result<'a> {
serde_json::to_string(&self.0).map(|string| { serde_json::to_string(&self.0).map(|string| {
content::Json(string).respond_to(req).unwrap() content::Json(string).respond_to(req).unwrap()
}).map_err(|e| { }).map_err(|e| {

View File

@ -65,7 +65,7 @@ pub extern crate tera;
pub mod json; pub mod json;
#[cfg(feature = "json")] #[cfg(feature = "json")]
pub use json::{Json, SerdeError, JsonValue}; pub use json::{Json, JsonError, JsonValue};
#[cfg(feature = "msgpack")] #[cfg(feature = "msgpack")]
#[doc(hidden)] #[doc(hidden)]