mirror of
https://github.com/fafhrd91/actix-web
synced 2025-08-31 17:07:01 +02:00
body ergonomics v3 (#2468)
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
convert::TryInto,
|
||||
error::Error as StdError,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use actix_http::{
|
||||
body::{AnyBody, BodyStream},
|
||||
body::{BodyStream, BoxBody, MessageBody},
|
||||
http::{
|
||||
header::{self, HeaderName, IntoHeaderPair, IntoHeaderValue},
|
||||
ConnectionType, Error as HttpError, StatusCode,
|
||||
@@ -26,14 +25,14 @@ use cookie::{Cookie, CookieJar};
|
||||
|
||||
use crate::{
|
||||
error::{Error, JsonPayloadError},
|
||||
HttpResponse,
|
||||
BoxError, HttpResponse,
|
||||
};
|
||||
|
||||
/// An HTTP response builder.
|
||||
///
|
||||
/// This type can be used to construct an instance of `Response` through a builder-like pattern.
|
||||
pub struct HttpResponseBuilder {
|
||||
res: Option<Response<AnyBody>>,
|
||||
res: Option<Response<BoxBody>>,
|
||||
err: Option<HttpError>,
|
||||
#[cfg(feature = "cookies")]
|
||||
cookies: Option<CookieJar>,
|
||||
@@ -44,7 +43,7 @@ impl HttpResponseBuilder {
|
||||
/// Create response builder
|
||||
pub fn new(status: StatusCode) -> Self {
|
||||
Self {
|
||||
res: Some(Response::new(status)),
|
||||
res: Some(Response::with_body(status, BoxBody::new(()))),
|
||||
err: None,
|
||||
#[cfg(feature = "cookies")]
|
||||
cookies: None,
|
||||
@@ -299,7 +298,6 @@ impl HttpResponseBuilder {
|
||||
}
|
||||
|
||||
/// Mutable reference to a the response's extensions
|
||||
#[inline]
|
||||
pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {
|
||||
self.res
|
||||
.as_mut()
|
||||
@@ -307,18 +305,20 @@ impl HttpResponseBuilder {
|
||||
.extensions_mut()
|
||||
}
|
||||
|
||||
/// Set a body and generate `Response`.
|
||||
/// Set a body and build the `HttpResponse`.
|
||||
///
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn body<B: Into<AnyBody>>(&mut self, body: B) -> HttpResponse<AnyBody> {
|
||||
match self.message_body(body.into()) {
|
||||
Ok(res) => res,
|
||||
pub fn body<B>(&mut self, body: B) -> HttpResponse<BoxBody>
|
||||
where
|
||||
B: MessageBody + 'static,
|
||||
{
|
||||
match self.message_body(body) {
|
||||
Ok(res) => res.map_into_boxed_body(),
|
||||
Err(err) => HttpResponse::from_error(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a body and generate `Response`.
|
||||
/// Set a body and build the `HttpResponse`.
|
||||
///
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
pub fn message_body<B>(&mut self, body: B) -> Result<HttpResponse<B>, Error> {
|
||||
@@ -332,7 +332,7 @@ impl HttpResponseBuilder {
|
||||
.expect("cannot reuse response builder")
|
||||
.set_body(body);
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[allow(unused_mut)] // mut is only unused when cookies are disabled
|
||||
let mut res = HttpResponse::from(res);
|
||||
|
||||
#[cfg(feature = "cookies")]
|
||||
@@ -348,19 +348,19 @@ impl HttpResponseBuilder {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn streaming<S, E>(&mut self, stream: S) -> HttpResponse
|
||||
where
|
||||
S: Stream<Item = Result<Bytes, E>> + 'static,
|
||||
E: Into<Box<dyn StdError>> + 'static,
|
||||
E: Into<BoxError> + 'static,
|
||||
{
|
||||
self.body(AnyBody::new_boxed(BodyStream::new(stream)))
|
||||
self.body(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.
|
||||
pub fn json(&mut self, value: impl Serialize) -> HttpResponse {
|
||||
@@ -376,18 +376,18 @@ impl HttpResponseBuilder {
|
||||
self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
|
||||
}
|
||||
|
||||
self.body(AnyBody::from(body))
|
||||
self.body(body)
|
||||
}
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an empty body and generate `Response`
|
||||
/// Set an empty body and build the `HttpResponse`.
|
||||
///
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn finish(&mut self) -> HttpResponse {
|
||||
self.body(AnyBody::empty())
|
||||
self.body(())
|
||||
}
|
||||
|
||||
/// This method construct new `HttpResponseBuilder`
|
||||
@@ -416,7 +416,7 @@ impl From<HttpResponseBuilder> for HttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HttpResponseBuilder> for Response<AnyBody> {
|
||||
impl From<HttpResponseBuilder> for Response<BoxBody> {
|
||||
fn from(mut builder: HttpResponseBuilder) -> Self {
|
||||
builder.finish().into()
|
||||
}
|
||||
@@ -435,12 +435,9 @@ mod tests {
|
||||
use actix_http::body;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
dev::AnyBody,
|
||||
http::{
|
||||
header::{self, HeaderValue, CONTENT_TYPE},
|
||||
StatusCode,
|
||||
},
|
||||
use crate::http::{
|
||||
header::{self, HeaderValue, CONTENT_TYPE},
|
||||
StatusCode,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -475,7 +472,7 @@ mod tests {
|
||||
fn test_content_type() {
|
||||
let resp = HttpResponseBuilder::new(StatusCode::OK)
|
||||
.content_type("text/plain")
|
||||
.body(AnyBody::empty());
|
||||
.body(Bytes::new());
|
||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use actix_http::{
|
||||
body::{AnyBody, MessageBody},
|
||||
body::{BoxBody, EitherBody, MessageBody},
|
||||
http::{header::HeaderMap, StatusCode},
|
||||
Extensions, Response, ResponseHead,
|
||||
};
|
||||
@@ -25,12 +25,12 @@ use {
|
||||
use crate::{error::Error, HttpResponseBuilder};
|
||||
|
||||
/// An outgoing response.
|
||||
pub struct HttpResponse<B = AnyBody> {
|
||||
pub struct HttpResponse<B = BoxBody> {
|
||||
res: Response<B>,
|
||||
pub(crate) error: Option<Error>,
|
||||
}
|
||||
|
||||
impl HttpResponse<AnyBody> {
|
||||
impl HttpResponse<BoxBody> {
|
||||
/// Constructs a response.
|
||||
#[inline]
|
||||
pub fn new(status: StatusCode) -> Self {
|
||||
@@ -227,8 +227,26 @@ impl<B> HttpResponse<B> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: into_body equivalent
|
||||
// TODO: into_boxed_body
|
||||
// TODO: docs for the body map methods below
|
||||
|
||||
#[inline]
|
||||
pub fn map_into_left_body<R>(self) -> HttpResponse<EitherBody<B, R>> {
|
||||
self.map_body(|_, body| EitherBody::left(body))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn map_into_right_body<L>(self) -> HttpResponse<EitherBody<L, B>> {
|
||||
self.map_body(|_, body| EitherBody::right(body))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn map_into_boxed_body(self) -> HttpResponse<BoxBody>
|
||||
where
|
||||
B: MessageBody + 'static,
|
||||
{
|
||||
// TODO: avoid double boxing with down-casting, if it improves perf
|
||||
self.map_body(|_, body| BoxBody::new(body))
|
||||
}
|
||||
|
||||
/// Extract response body
|
||||
pub fn into_body(self) -> B {
|
||||
@@ -273,14 +291,14 @@ impl<B> From<HttpResponse<B>> for Response<B> {
|
||||
}
|
||||
}
|
||||
|
||||
// Future is only implemented for AnyBody payload type because it's the most useful for making
|
||||
// Future is only implemented for BoxBody payload type because it's the most useful for making
|
||||
// simple handlers without async blocks. Making it generic over all MessageBody types requires a
|
||||
// future impl on Response which would cause it's body field to be, undesirably, Option<B>.
|
||||
//
|
||||
// This impl is not particularly efficient due to the Response construction and should probably
|
||||
// not be invoked if performance is important. Prefer an async fn/block in such cases.
|
||||
impl Future for HttpResponse<AnyBody> {
|
||||
type Output = Result<Response<AnyBody>, Error>;
|
||||
impl Future for HttpResponse<BoxBody> {
|
||||
type Output = Result<Response<BoxBody>, Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if let Some(err) = self.error.take() {
|
||||
|
Reference in New Issue
Block a user