rouille/input/
json.rs

1// Copyright (c) 2016 The Rouille developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! Parsing JSON data in the body of a request.
11//!
12//! Returns an error if the content-type of the request is not JSON, if the JSON is malformed,
13//! or if a field is missing or fails to parse.
14//!
15//! # Example
16//!
17//! ```
18//! # extern crate serde;
19//! # #[macro_use] extern crate serde_derive;
20//! # #[macro_use] extern crate rouille;
21//! # use rouille::{Request, Response};
22//! # fn main() {}
23//!
24//! fn route_handler(request: &Request) -> Response {
25//!     #[derive(Deserialize)]
26//!     struct Json {
27//!         field1: String,
28//!         field2: i32,
29//!     }
30//!
31//!     let json: Json = try_or_400!(rouille::input::json_input(request));
32//!     Response::text(format!("field1's value is {}", json.field1))
33//! }
34//! ```
35//!
36
37use serde;
38use serde_json;
39use std::error;
40use std::fmt;
41use std::io::Error as IoError;
42use Request;
43
44/// Error that can happen when parsing the JSON input.
45#[derive(Debug)]
46pub enum JsonError {
47    /// Can't parse the body of the request because it was already extracted.
48    BodyAlreadyExtracted,
49
50    /// Wrong content type.
51    WrongContentType,
52
53    /// Could not read the body from the request. Also happens if the body is not valid UTF-8.
54    IoError(IoError),
55
56    /// Error while parsing.
57    ParseError(serde_json::Error),
58}
59
60impl From<IoError> for JsonError {
61    fn from(err: IoError) -> JsonError {
62        JsonError::IoError(err)
63    }
64}
65
66impl From<serde_json::Error> for JsonError {
67    fn from(err: serde_json::Error) -> JsonError {
68        JsonError::ParseError(err)
69    }
70}
71
72impl error::Error for JsonError {
73    #[inline]
74    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
75        match *self {
76            JsonError::IoError(ref e) => Some(e),
77            JsonError::ParseError(ref e) => Some(e),
78            _ => None,
79        }
80    }
81}
82
83impl fmt::Display for JsonError {
84    #[inline]
85    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
86        let description = match *self {
87            JsonError::BodyAlreadyExtracted => "the body of the request was already extracted",
88            JsonError::WrongContentType => "the request didn't have a JSON content type",
89            JsonError::IoError(_) => {
90                "could not read the body from the request, or could not execute the CGI program"
91            }
92            JsonError::ParseError(_) => "error while parsing the JSON body",
93        };
94
95        write!(fmt, "{}", description)
96    }
97}
98
99/// Attempts to parse the request's body as JSON.
100///
101/// Returns an error if the content-type of the request is not JSON, or if the JSON is malformed.
102///
103/// # Example
104///
105/// ```
106/// # extern crate serde;
107/// # #[macro_use] extern crate serde_derive;
108/// # #[macro_use] extern crate rouille;
109/// # use rouille::{Request, Response};
110/// fn main() {}
111///
112/// fn route_handler(request: &Request) -> Response {
113///     #[derive(Deserialize)]
114///     struct Json {
115///         field1: String,
116///         field2: i32,
117///     }
118///
119///     let json: Json = try_or_400!(rouille::input::json_input(request));
120///     Response::text(format!("field1's value is {}", json.field1))
121/// }
122/// ```
123///
124pub fn json_input<O>(request: &Request) -> Result<O, JsonError>
125where
126    O: serde::de::DeserializeOwned,
127{
128    // TODO: add an optional bytes limit
129
130    if let Some(header) = request.header("Content-Type") {
131        if !header.starts_with("application/json") {
132            return Err(JsonError::WrongContentType);
133        }
134    } else {
135        return Err(JsonError::WrongContentType);
136    }
137
138    if let Some(b) = request.data() {
139        serde_json::from_reader::<_, O>(b).map_err(From::from)
140    } else {
141        Err(JsonError::BodyAlreadyExtracted)
142    }
143}