responder body type temp

This commit is contained in:
Rob Ede 2021-11-29 23:57:29 +00:00
parent e4e2cef2e1
commit 81d6fb4d65
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
16 changed files with 203 additions and 73 deletions

View File

@ -6,8 +6,7 @@ use std::{
task::{Context, Poll}, task::{Context, Poll},
}; };
use actix_web::error::Error; use actix_web::{error::Error, web::Bytes};
use bytes::Bytes;
use futures_core::{ready, Stream}; use futures_core::{ready, Stream};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

View File

@ -13,6 +13,7 @@ use std::os::unix::fs::MetadataExt;
use actix_http::body::AnyBody; use actix_http::body::AnyBody;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_web::{ use actix_web::{
body::BoxBody,
dev::{ dev::{
AppService, BodyEncoding, HttpServiceFactory, ResourceDef, ServiceRequest, AppService, BodyEncoding, HttpServiceFactory, ResourceDef, ServiceRequest,
ServiceResponse, SizedStream, ServiceResponse, SizedStream,
@ -394,7 +395,7 @@ impl NamedFile {
} }
/// Creates an `HttpResponse` with file as a streaming body. /// Creates an `HttpResponse` with file as a streaming body.
pub fn into_response(self, req: &HttpRequest) -> HttpResponse { pub fn into_response(self, req: &HttpRequest) -> HttpResponse<BoxBody> {
if self.status_code != StatusCode::OK { if self.status_code != StatusCode::OK {
let mut res = HttpResponse::build(self.status_code); let mut res = HttpResponse::build(self.status_code);
@ -598,7 +599,10 @@ impl DerefMut for NamedFile {
} }
impl Responder for NamedFile { impl Responder for NamedFile {
fn respond_to(self, req: &HttpRequest) -> HttpResponse { // TODO: can be improved
type Body = BoxBody;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
self.into_response(req) self.into_response(req)
} }
} }

View File

@ -77,7 +77,10 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn either_body_works() { fn type_parameter_inference() {
let _body = EitherBody::new(()); let _body: EitherBody<(), _> = EitherBody::new(());
let _body: EitherBody<_, ()> = EitherBody::left(());
let _body: EitherBody<(), _> = EitherBody::right(());
} }
} }

View File

@ -13,7 +13,8 @@ use pin_project_lite::pin_project;
use super::BodySize; use super::BodySize;
/// An interface for response bodies. /// An interface types that can converted to bytes and used as response bodies.
// TODO: examples
pub trait MessageBody { pub trait MessageBody {
type Error; type Error;

View File

@ -18,7 +18,7 @@ pub enum BodySize {
} }
impl BodySize { impl BodySize {
/// Returns true if size hint indicates no or empty body. /// Returns true if size hint indicates omitted or empty body.
/// ///
/// Streams will return false because it cannot be known without reading the stream. /// Streams will return false because it cannot be known without reading the stream.
/// ///

View File

@ -109,7 +109,9 @@ impl<T> Responder for InternalError<T>
where where
T: fmt::Debug + fmt::Display + 'static, T: fmt::Debug + fmt::Display + 'static,
{ {
fn respond_to(self, _: &HttpRequest) -> HttpResponse { type Body = BoxBody;
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::from_error(self) HttpResponse::from_error(self)
} }
} }

View File

@ -6,6 +6,7 @@ use actix_service::{
}; };
use crate::{ use crate::{
body::EitherBody,
service::{ServiceRequest, ServiceResponse}, service::{ServiceRequest, ServiceResponse},
Error, FromRequest, HttpResponse, Responder, Error, FromRequest, HttpResponse, Responder,
}; };
@ -26,7 +27,13 @@ where
pub fn handler_service<F, T, R>( pub fn handler_service<F, T, R>(
handler: F, handler: F,
) -> BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()> ) -> BoxServiceFactory<
(),
ServiceRequest,
ServiceResponse<EitherBody<<R::Output as Responder>::Body>>,
Error,
(),
>
where where
F: Handler<T, R>, F: Handler<T, R>,
T: FromRequest, T: FromRequest,
@ -35,12 +42,21 @@ where
{ {
boxed::factory(fn_service(move |req: ServiceRequest| { boxed::factory(fn_service(move |req: ServiceRequest| {
let handler = handler.clone(); let handler = handler.clone();
async move { async move {
let (req, mut payload) = req.into_parts(); let (req, mut payload) = req.into_parts();
let res = match T::from_request(&req, &mut payload).await { let res = match T::from_request(&req, &mut payload).await {
Err(err) => HttpResponse::from_error(err), Err(err) => {
Ok(data) => handler.call(data).await.respond_to(&req), HttpResponse::from_error(err).map_body(|_, body| EitherBody::right(body))
}
Ok(data) => handler
.call(data)
.await
.respond_to(&req)
.map_body(|_, body| EitherBody::left(body)),
}; };
Ok(ServiceResponse::new(req, res)) Ok(ServiceResponse::new(req, res))
} }
})) }))

View File

@ -1,7 +1,4 @@
use std::cell::RefCell; use std::{cell::RefCell, error::Error as StdError, fmt, future::Future, rc::Rc};
use std::fmt;
use std::future::Future;
use std::rc::Rc;
use actix_http::Extensions; use actix_http::Extensions;
use actix_router::{IntoPatterns, Patterns}; use actix_router::{IntoPatterns, Patterns};
@ -13,6 +10,7 @@ use futures_core::future::LocalBoxFuture;
use futures_util::future::join_all; use futures_util::future::join_all;
use crate::{ use crate::{
body::MessageBody,
data::Data, data::Data,
dev::{ensure_leading_slash, AppService, ResourceDef}, dev::{ensure_leading_slash, AppService, ResourceDef},
guard::Guard, guard::Guard,
@ -241,6 +239,9 @@ where
I: FromRequest + 'static, I: FromRequest + 'static,
R: Future + 'static, R: Future + 'static,
R::Output: Responder + 'static, R::Output: Responder + 'static,
<R::Output as Responder>::Body: MessageBody + 'static,
<<R::Output as Responder>::Body as MessageBody>::Error:
Into<Box<dyn StdError + 'static>>,
{ {
self.routes.push(Route::new().to(handler)); self.routes.push(Route::new().to(handler));
self self

View File

@ -1,5 +1,7 @@
use std::error::Error as StdError;
use actix_http::{ use actix_http::{
body::BoxBody, body::{BoxBody, EitherBody, MessageBody},
http::{header::IntoHeaderPair, Error as HttpError, HeaderMap, StatusCode}, http::{header::IntoHeaderPair, Error as HttpError, HeaderMap, StatusCode},
}; };
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
@ -10,8 +12,10 @@ use crate::{Error, HttpRequest, HttpResponse, HttpResponseBuilder};
/// ///
/// Any types that implement this trait can be used in the return type of a handler. /// Any types that implement this trait can be used in the return type of a handler.
pub trait Responder { pub trait Responder {
type Body: MessageBody + 'static;
/// Convert self to `HttpResponse`. /// Convert self to `HttpResponse`.
fn respond_to(self, req: &HttpRequest) -> HttpResponse<BoxBody>; fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body>;
/// Override a status code for a Responder. /// Override a status code for a Responder.
/// ///
@ -57,38 +61,56 @@ pub trait Responder {
} }
impl Responder for HttpResponse { impl Responder for HttpResponse {
type Body = BoxBody;
#[inline] #[inline]
fn respond_to(self, _: &HttpRequest) -> HttpResponse<BoxBody> { fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
self self
} }
} }
impl Responder for actix_http::Response<BoxBody> { impl Responder for actix_http::Response<BoxBody> {
type Body = BoxBody;
#[inline] #[inline]
fn respond_to(self, _: &HttpRequest) -> HttpResponse<BoxBody> { fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::from(self) HttpResponse::from(self)
} }
} }
impl Responder for HttpResponseBuilder { impl Responder for HttpResponseBuilder {
type Body = BoxBody;
#[inline] #[inline]
fn respond_to(mut self, _: &HttpRequest) -> HttpResponse<BoxBody> { fn respond_to(mut self, _: &HttpRequest) -> HttpResponse<Self::Body> {
self.finish() self.finish()
} }
} }
impl Responder for actix_http::ResponseBuilder { impl Responder for actix_http::ResponseBuilder {
type Body = BoxBody;
#[inline] #[inline]
fn respond_to(mut self, req: &HttpRequest) -> HttpResponse<BoxBody> { fn respond_to(mut self, req: &HttpRequest) -> HttpResponse<Self::Body> {
self.finish().map_into_boxed_body().respond_to(req) self.finish().map_into_boxed_body().respond_to(req)
} }
} }
impl<T: Responder> Responder for Option<T> { impl<T> Responder for Option<T>
fn respond_to(self, req: &HttpRequest) -> HttpResponse<BoxBody> { where
T: Responder,
<T::Body as MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
{
type Body = EitherBody<T::Body>;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
match self { match self {
Some(val) => val.respond_to(req), Some(val) => val
None => HttpResponse::new(StatusCode::NOT_FOUND), .respond_to(req)
.map_body(|_, body| EitherBody::left(body)),
None => HttpResponse::new(StatusCode::NOT_FOUND)
.map_body(|_, body| EitherBody::right(body)),
} }
} }
} }
@ -96,48 +118,78 @@ impl<T: Responder> Responder for Option<T> {
impl<T, E> Responder for Result<T, E> impl<T, E> Responder for Result<T, E>
where where
T: Responder, T: Responder,
<T::Body as MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
E: Into<Error>, E: Into<Error>,
{ {
fn respond_to(self, req: &HttpRequest) -> HttpResponse<BoxBody> { type Body = EitherBody<T::Body>;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
match self { match self {
Ok(val) => val.respond_to(req), Ok(val) => val
Err(e) => HttpResponse::from_error(e.into()), .respond_to(req)
.map_body(|_, body| EitherBody::left(body)),
Err(e) => {
HttpResponse::from_error(e.into()).map_body(|_, body| EitherBody::right(body))
}
} }
} }
} }
impl<T: Responder> Responder for (T, StatusCode) { impl<T: Responder> Responder for (T, StatusCode) {
fn respond_to(self, req: &HttpRequest) -> HttpResponse<BoxBody> { type Body = T::Body;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
let mut res = self.0.respond_to(req); let mut res = self.0.respond_to(req);
*res.status_mut() = self.1; *res.status_mut() = self.1;
res res
} }
} }
macro_rules! impl_responder { macro_rules! impl_responder_by_forward_into_base_response {
($res: ty, $ct: path) => { ($res:ty, $body:ty) => {
impl Responder for $res { impl Responder for $res {
fn respond_to(self, _: &HttpRequest) -> HttpResponse<BoxBody> { type Body = $body;
HttpResponse::Ok().content_type($ct).body(self)
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
let res: actix_http::Response<_> = self.into();
res.into()
} }
} }
}; };
($res:ty) => {
impl_responder_by_forward_into_base_response!($res, $res);
};
} }
impl_responder!(&'static str, mime::TEXT_PLAIN_UTF_8); impl_responder_by_forward_into_base_response!(&'static [u8]);
impl_responder_by_forward_into_base_response!(Bytes);
impl_responder_by_forward_into_base_response!(BytesMut);
impl_responder!(String, mime::TEXT_PLAIN_UTF_8); impl_responder_by_forward_into_base_response!(&'static str);
impl_responder_by_forward_into_base_response!(String);
// macro_rules! impl_responder {
// ($res:ty, $body:ty, $ct:path) => {
// impl Responder for $res {
// type Body = $body;
// fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
// HttpResponse::Ok().content_type($ct).body(self)
// }
// }
// };
// ($res:ty, $ct:path) => {
// impl_responder!($res, $res, $ct);
// };
// }
// impl_responder!(&'_ String, mime::TEXT_PLAIN_UTF_8); // impl_responder!(&'_ String, mime::TEXT_PLAIN_UTF_8);
// impl_responder!(Cow<'_, str>, mime::TEXT_PLAIN_UTF_8); // impl_responder!(Cow<'_, str>, mime::TEXT_PLAIN_UTF_8);
impl_responder!(&'static [u8], mime::APPLICATION_OCTET_STREAM);
impl_responder!(Bytes, mime::APPLICATION_OCTET_STREAM);
impl_responder!(BytesMut, mime::APPLICATION_OCTET_STREAM);
/// Allows overriding status code and headers for a responder. /// Allows overriding status code and headers for a responder.
pub struct CustomResponder<T> { pub struct CustomResponder<T> {
responder: T, responder: T,
@ -202,11 +254,20 @@ impl<T: Responder> CustomResponder<T> {
} }
} }
impl<T: Responder> Responder for CustomResponder<T> { impl<T> Responder for CustomResponder<T>
fn respond_to(self, req: &HttpRequest) -> HttpResponse { where
T: Responder,
<T::Body as MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
{
type Body = EitherBody<T::Body>;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
let headers = match self.headers { let headers = match self.headers {
Ok(headers) => headers, Ok(headers) => headers,
Err(err) => return HttpResponse::from_error(Error::from(err)), Err(err) => {
return HttpResponse::from_error(Error::from(err))
.map_body(|_, body| EitherBody::right(body))
}
}; };
let mut res = self.responder.respond_to(req); let mut res = self.responder.respond_to(req);
@ -220,7 +281,7 @@ impl<T: Responder> Responder for CustomResponder<T> {
res.headers_mut().insert(k, v); res.headers_mut().insert(k, v);
} }
res res.map_body(|_, body| EitherBody::left(body))
} }
} }

View File

@ -306,7 +306,7 @@ impl HttpResponseBuilder {
.extensions_mut() .extensions_mut()
} }
/// Set a body and generate `Response`. /// Set a body and build the `HttpResponse`.
/// ///
/// `HttpResponseBuilder` can not be used after this call. /// `HttpResponseBuilder` can not be used after this call.
pub fn body<B>(&mut self, body: B) -> HttpResponse<BoxBody> pub fn body<B>(&mut self, body: B) -> HttpResponse<BoxBody>
@ -320,7 +320,7 @@ impl HttpResponseBuilder {
} }
} }
/// Set a body and generate `Response`. /// Set a body and build the `HttpResponse`.
/// ///
/// `HttpResponseBuilder` can not be used after this call. /// `HttpResponseBuilder` can not be used after this call.
pub fn message_body<B>(&mut self, body: B) -> Result<HttpResponse<B>, Error> { pub fn message_body<B>(&mut self, body: B) -> Result<HttpResponse<B>, Error> {
@ -350,7 +350,7 @@ impl HttpResponseBuilder {
Ok(res) Ok(res)
} }
/// Set a streaming body and generate `Response`. /// Set a streaming body and build the `HttpResponse`.
/// ///
/// `HttpResponseBuilder` can not be used after this call. /// `HttpResponseBuilder` can not be used after this call.
#[inline] #[inline]
@ -362,7 +362,7 @@ impl HttpResponseBuilder {
self.body(BoxBody::new(BodyStream::new(stream))) self.body(BoxBody::new(BodyStream::new(stream)))
} }
/// Set a json body and generate `Response` /// Set a JSON body and build the `HttpResponse`.
/// ///
/// `HttpResponseBuilder` can not be used after this call. /// `HttpResponseBuilder` can not be used after this call.
pub fn json(&mut self, value: impl Serialize) -> HttpResponse { pub fn json(&mut self, value: impl Serialize) -> HttpResponse {
@ -384,7 +384,7 @@ impl HttpResponseBuilder {
} }
} }
/// Set an empty body and generate `Response` /// Set an empty body and build the `HttpResponse`.
/// ///
/// `HttpResponseBuilder` can not be used after this call. /// `HttpResponseBuilder` can not be used after this call.
#[inline] #[inline]

View File

@ -1,6 +1,6 @@
#![allow(clippy::rc_buffer)] // inner value is mutated before being shared (`Rc::get_mut`) #![allow(clippy::rc_buffer)] // inner value is mutated before being shared (`Rc::get_mut`)
use std::{future::Future, rc::Rc}; use std::{error::Error as StdError, future::Future, mem, rc::Rc};
use actix_http::http::Method; use actix_http::http::Method;
use actix_service::{ use actix_service::{
@ -10,6 +10,7 @@ use actix_service::{
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use crate::{ use crate::{
body::MessageBody,
guard::{self, Guard}, guard::{self, Guard},
handler::{handler_service, Handler}, handler::{handler_service, Handler},
service::{ServiceRequest, ServiceResponse}, service::{ServiceRequest, ServiceResponse},
@ -30,13 +31,16 @@ impl Route {
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
pub fn new() -> Route { pub fn new() -> Route {
Route { Route {
service: handler_service(HttpResponse::NotFound), // TODO: remove double boxing
service: boxed::factory(
handler_service(HttpResponse::NotFound).map(|res| res.map_into_boxed_body()),
),
guards: Rc::new(Vec::new()), guards: Rc::new(Vec::new()),
} }
} }
pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> { pub(crate) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
std::mem::take(Rc::get_mut(&mut self.guards).unwrap()) mem::take(Rc::get_mut(&mut self.guards).unwrap())
} }
} }
@ -181,8 +185,13 @@ impl Route {
T: FromRequest + 'static, T: FromRequest + 'static,
R: Future + 'static, R: Future + 'static,
R::Output: Responder + 'static, R::Output: Responder + 'static,
<R::Output as Responder>::Body: MessageBody + 'static,
<<R::Output as Responder>::Body as MessageBody>::Error:
Into<Box<dyn StdError + 'static>>,
{ {
self.service = handler_service(handler); // TODO: remove double boxing
self.service =
boxed::factory(handler_service(handler).map(|res| res.map_into_boxed_body()));
self self
} }

View File

@ -656,8 +656,8 @@ fn create_tcp_listener(addr: net::SocketAddr, backlog: u32) -> io::Result<net::T
Ok(net::TcpListener::from(socket)) Ok(net::TcpListener::from(socket))
} }
#[cfg(feature = "openssl")]
/// Configure `SslAcceptorBuilder` with custom server flags. /// Configure `SslAcceptorBuilder` with custom server flags.
#[cfg(feature = "openssl")]
fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result<SslAcceptor> { fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result<SslAcceptor> {
builder.set_alpn_select_callback(|_, protocols| { builder.set_alpn_select_callback(|_, protocols| {
const H2: &[u8] = b"\x02h2"; const H2: &[u8] = b"\x02h2";

View File

@ -1,6 +1,7 @@
//! For either helper, see [`Either`]. //! For either helper, see [`Either`].
use std::{ use std::{
error::Error as StdError,
future::Future, future::Future,
mem, mem,
pin::Pin, pin::Pin,
@ -12,7 +13,7 @@ use futures_core::ready;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::{ use crate::{
dev, body, dev,
web::{Form, Json}, web::{Form, Json},
Error, FromRequest, HttpRequest, HttpResponse, Responder, Error, FromRequest, HttpRequest, HttpResponse, Responder,
}; };
@ -144,12 +145,21 @@ impl<L, R> Either<L, R> {
impl<L, R> Responder for Either<L, R> impl<L, R> Responder for Either<L, R>
where where
L: Responder, L: Responder,
<L::Body as dev::MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
R: Responder, R: Responder,
<R::Body as dev::MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
{ {
fn respond_to(self, req: &HttpRequest) -> HttpResponse { type Body = body::EitherBody<L::Body, R::Body>;
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
match self { match self {
Either::Left(a) => a.respond_to(req), Either::Left(a) => a
Either::Right(b) => b.respond_to(req), .respond_to(req)
.map_body(|_, body| body::EitherBody::left(body)),
Either::Right(b) => b
.respond_to(req)
.map_body(|_, body| body::EitherBody::right(body)),
} }
} }
} }

View File

@ -20,8 +20,9 @@ use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "__compress")] #[cfg(feature = "__compress")]
use crate::dev::Decompress; use crate::dev::Decompress;
use crate::{ use crate::{
error::UrlencodedError, extract::FromRequest, http::header::CONTENT_LENGTH, web, Error, body::EitherBody, error::UrlencodedError, extract::FromRequest,
HttpMessage, HttpRequest, HttpResponse, Responder, http::header::CONTENT_LENGTH, web, Error, HttpMessage, HttpRequest, HttpResponse,
Responder,
}; };
/// URL encoded payload extractor and responder. /// URL encoded payload extractor and responder.
@ -180,12 +181,22 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
/// See [here](#responder) for example of usage as a handler return type. /// See [here](#responder) for example of usage as a handler return type.
impl<T: Serialize> Responder for Form<T> { impl<T: Serialize> Responder for Form<T> {
fn respond_to(self, _: &HttpRequest) -> HttpResponse { type Body = EitherBody<String>;
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
match serde_urlencoded::to_string(&self.0) { match serde_urlencoded::to_string(&self.0) {
Ok(body) => HttpResponse::Ok() Ok(body) => match HttpResponse::Ok()
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED) .content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
.body(body), .message_body(body)
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err)), {
Ok(res) => res.map_body(|_, body| EitherBody::left(body)),
Err(err) => {
HttpResponse::from_error(err).map_body(|_, body| EitherBody::right(body))
}
},
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err))
.map_body(|_, body| EitherBody::right(body)),
} }
} }
} }

View File

@ -19,6 +19,7 @@ use actix_http::Payload;
#[cfg(feature = "__compress")] #[cfg(feature = "__compress")]
use crate::dev::Decompress; use crate::dev::Decompress;
use crate::{ use crate::{
body::EitherBody,
error::{Error, JsonPayloadError}, error::{Error, JsonPayloadError},
extract::FromRequest, extract::FromRequest,
http::header::CONTENT_LENGTH, http::header::CONTENT_LENGTH,
@ -116,12 +117,22 @@ impl<T: Serialize> Serialize for Json<T> {
/// ///
/// If serialization failed /// If serialization failed
impl<T: Serialize> Responder for Json<T> { impl<T: Serialize> Responder for Json<T> {
fn respond_to(self, _: &HttpRequest) -> HttpResponse { type Body = EitherBody<String>;
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
match serde_json::to_string(&self.0) { match serde_json::to_string(&self.0) {
Ok(body) => HttpResponse::Ok() Ok(body) => match HttpResponse::Ok()
.content_type(mime::APPLICATION_JSON) .content_type(mime::APPLICATION_JSON)
.body(body), .message_body(body)
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)), {
Ok(res) => res.map_body(|_, body| EitherBody::left(body)),
Err(err) => {
HttpResponse::from_error(err).map_body(|_, body| EitherBody::right(body))
}
},
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err))
.map_body(|_, body| EitherBody::right(body)),
} }
} }
} }

View File

@ -1,14 +1,14 @@
//! Essentials helper functions and types for application registration. //! Essentials helper functions and types for application registration.
use std::future::Future; use std::{error::Error as StdError, future::Future};
use actix_http::http::Method; use actix_http::http::Method;
use actix_router::IntoPatterns; use actix_router::IntoPatterns;
pub use bytes::{Buf, BufMut, Bytes, BytesMut}; pub use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::{ use crate::{
error::BlockingError, extract::FromRequest, handler::Handler, resource::Resource, body::MessageBody, error::BlockingError, extract::FromRequest, handler::Handler,
responder::Responder, route::Route, scope::Scope, service::WebService, resource::Resource, responder::Responder, route::Route, scope::Scope, service::WebService,
}; };
pub use crate::config::ServiceConfig; pub use crate::config::ServiceConfig;
@ -145,6 +145,8 @@ where
I: FromRequest + 'static, I: FromRequest + 'static,
R: Future + 'static, R: Future + 'static,
R::Output: Responder + 'static, R::Output: Responder + 'static,
<R::Output as Responder>::Body: MessageBody + 'static,
<<R::Output as Responder>::Body as MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
{ {
Route::new().to(handler) Route::new().to(handler)
} }