From 656595711ca2940f81f980d80de69346605532c2 Mon Sep 17 00:00:00 2001 From: Dominic Date: Sat, 19 Oct 2019 20:23:25 +0200 Subject: [PATCH] response can have a different mime type than json --- gotham_restful/src/result.rs | 93 ++++++++++++++++++++++++----------- gotham_restful/src/routing.rs | 36 ++++++++++---- 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/gotham_restful/src/result.rs b/gotham_restful/src/result.rs index 6774473..6158147 100644 --- a/gotham_restful/src/result.rs +++ b/gotham_restful/src/result.rs @@ -6,12 +6,40 @@ use serde::Serialize; use serde_json::error::Error as SerdeJsonError; use std::error::Error; +pub struct Response +{ + pub status : StatusCode, + pub body : String, + pub mime : Option +} + +impl Response +{ + pub fn json(status : StatusCode, body : String) -> Self + { + Self { + status, + body, + mime: Some(APPLICATION_JSON) + } + } + + pub fn no_content() -> Self + { + Self { + status: StatusCode::NO_CONTENT, + body: String::new(), + mime: None + } + } +} + /// A trait provided to convert a resource's result to json. pub trait ResourceResult { /// Turn this into a response that can be returned to the browser. This api will likely /// change in the future. - fn to_response(&self) -> Result<(StatusCode, String), SerdeJsonError>; + fn to_response(&self) -> Result; /// Return a list of supported mime types. fn accepted_types() -> Option> @@ -59,13 +87,13 @@ impl From for ResourceError impl ResourceResult for Result { - fn to_response(&self) -> Result<(StatusCode, String), SerdeJsonError> + fn to_response(&self) -> Result { Ok(match self { - Ok(r) => (StatusCode::OK, serde_json::to_string(r)?), + Ok(r) => Response::json(StatusCode::OK, serde_json::to_string(r)?), Err(e) => { let err : ResourceError = e.into(); - (StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?) + Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?) } }) } @@ -119,9 +147,9 @@ impl From for Success impl ResourceResult for Success { - fn to_response(&self) -> Result<(StatusCode, String), SerdeJsonError> + fn to_response(&self) -> Result { - Ok((StatusCode::OK, serde_json::to_string(&self.0)?)) + Ok(Response::json(StatusCode::OK, serde_json::to_string(&self.0)?)) } fn accepted_types() -> Option> @@ -169,9 +197,9 @@ impl From<()> for NoContent impl ResourceResult for NoContent { /// This will always be a _204 No Content_ together with an empty string. - fn to_response(&self) -> Result<(StatusCode, String), SerdeJsonError> + fn to_response(&self) -> Result { - Ok((Self::default_status(), "".to_string())) + Ok(Response::no_content()) } /// Returns the schema of the `()` type. @@ -191,15 +219,15 @@ impl ResourceResult for NoContent impl ResourceResult for Result { - fn to_response(&self) -> Result<(StatusCode, String), SerdeJsonError> + fn to_response(&self) -> Result { - Ok(match self { - Ok(_) => (Self::default_status(), "".to_string()), + match self { + Ok(nc) => nc.to_response(), Err(e) => { let err : ResourceError = e.into(); - (StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?) + Ok(Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?)) } - }) + } } #[cfg(feature = "openapi")] @@ -236,44 +264,49 @@ mod test fn resource_result_ok() { let ok : Result = Ok(Msg::default()); - let (status, json) = ok.to_response().expect("didn't expect error response"); - assert_eq!(status, StatusCode::OK); - assert_eq!(json, r#"{"msg":""}"#); + let res = ok.to_response().expect("didn't expect error response"); + assert_eq!(res.status, StatusCode::OK); + assert_eq!(res.body, r#"{"msg":""}"#); + assert_eq!(res.mime, Some(APPLICATION_JSON)); } #[test] fn resource_result_err() { let err : Result = Err(MsgError::default()); - let (status, json) = err.to_response().expect("didn't expect error response"); - assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR); - assert_eq!(json, format!(r#"{{"error":true,"message":"{}"}}"#, err.unwrap_err())); + let res = err.to_response().expect("didn't expect error response"); + assert_eq!(res.status, StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(res.body, format!(r#"{{"error":true,"message":"{}"}}"#, err.unwrap_err())); + assert_eq!(res.mime, Some(APPLICATION_JSON)); } #[test] fn success_always_successfull() { let success : Success = Msg::default().into(); - let (status, json) = success.to_response().expect("didn't expect error response"); - assert_eq!(status, StatusCode::OK); - assert_eq!(json, r#"{"msg":""}"#); + let res = success.to_response().expect("didn't expect error response"); + assert_eq!(res.status, StatusCode::OK); + assert_eq!(res.body, r#"{"msg":""}"#); + assert_eq!(res.mime, Some(APPLICATION_JSON)); } #[test] - fn no_content_has_empty_json() + fn no_content_has_empty_response() { let no_content = NoContent::default(); - let (status, json) = no_content.to_response().expect("didn't expect error response"); - assert_eq!(status, StatusCode::NO_CONTENT); - assert_eq!(json, ""); + let res = no_content.to_response().expect("didn't expect error response"); + assert_eq!(res.status, StatusCode::NO_CONTENT); + assert_eq!(res.body, ""); + assert_eq!(res.mime, None); } #[test] fn no_content_result() { - let no_content = NoContent::default(); - let res_def = no_content.to_response().expect("didn't expect error response"); - let res_err = Result::::Ok(no_content).to_response().expect("didn't expect error response"); - assert_eq!(res_def, res_err); + let no_content : Result = Ok(NoContent::default()); + let res = no_content.to_response().expect("didn't expect error response"); + assert_eq!(res.status, StatusCode::NO_CONTENT); + assert_eq!(res.body, ""); + assert_eq!(res.mime, None); } } diff --git a/gotham_restful/src/routing.rs b/gotham_restful/src/routing.rs index 7e60201..400dfa8 100644 --- a/gotham_restful/src/routing.rs +++ b/gotham_restful/src/routing.rs @@ -1,6 +1,6 @@ use crate::{ resource::*, - result::{ResourceError, ResourceResult}, + result::{ResourceError, ResourceResult, Response}, ResourceType, StatusCode }; @@ -14,7 +14,7 @@ use futures::{ use gotham::{ extractor::QueryStringExtractor, handler::{HandlerFuture, IntoHandlerError}, - helpers::http::response::create_response, + helpers::http::response::{create_empty_response, create_response}, pipeline::chain::PipelineHandleChain, router::{ builder::*, @@ -23,7 +23,11 @@ use gotham::{ }, state::{FromState, State} }; -use hyper::Body; +use hyper::{ + header::CONTENT_TYPE, + Body, + Method +}; use mime::{Mime, APPLICATION_JSON}; use serde::de::DeserializeOwned; use std::panic::RefUnwindSafe; @@ -108,6 +112,20 @@ pub trait DrawResourceRoutes Handler : ResourceDelete; } +fn response_from(res : Response, state : &State) -> hyper::Response +{ + let mut r = create_empty_response(state, res.status); + if let Some(mime) = res.mime + { + r.headers_mut().insert(CONTENT_TYPE, mime.as_ref().parse().unwrap()); + } + if Method::borrow_from(state) != Method::HEAD + { + *r.body_mut() = res.body.into(); + } + r +} + fn to_handler_future(mut state : State, get_result : F) -> Box where F : FnOnce(&mut State) -> R, @@ -115,9 +133,9 @@ where { let res = get_result(&mut state).to_response(); match res { - Ok((status, body)) => { - let res = create_response(&state, status, APPLICATION_JSON, body); - Box::new(ok((state, res))) + Ok(res) => { + let r = response_from(res, &state); + Box::new(ok((state, r))) }, Err(e) => Box::new(err((state, e.into_handler_error()))) } @@ -154,9 +172,9 @@ where let res = get_result(&mut state, body).to_response(); match res { - Ok((status, body)) => { - let res = create_response(&state, status, APPLICATION_JSON, body); - ok((state, res)) + Ok(res) => { + let r = response_from(res, &state); + ok((state, r)) }, Err(e) => err((state, e.into_handler_error())) }