ntex 4.0.0-beta.10

Framework for composable network services
Documentation
use std::fmt;

use crate::http::body::{Body, MessageBody, ResponseBody};
use crate::http::{HeaderMap, Response, ResponseHead, StatusCode};

use super::{AppState, HttpRequest, WebResponseError};

/// An http service response.
pub struct WebResponse {
    request: HttpRequest,
    response: Response<Body>,
}

impl WebResponse {
    /// Create web response instance.
    pub fn new(response: Response<Body>, request: HttpRequest) -> Self {
        WebResponse { request, response }
    }

    #[must_use]
    /// Create web response from the error.
    pub fn from_err<St>(
        st: &St,
        mut err: impl WebResponseError<St, St::Error>,
        request: HttpRequest,
    ) -> Self
    where
        St: AppState,
    {
        let res = err.error_response(st);

        if res.head().status == StatusCode::INTERNAL_SERVER_ERROR {
            log::error!("Internal Server Error: {err:?}");
        } else {
            log::debug!("Error in response: {err:?}");
        }

        WebResponse {
            request,
            response: res.into_body(),
        }
    }

    #[inline]
    #[must_use]
    /// Create web response for error.
    pub fn error_response<St>(self, st: &St, err: impl WebResponseError<St, St::Error>) -> Self
    where
        St: AppState,
    {
        Self::from_err(st, err, self.request)
    }

    #[inline]
    #[must_use]
    /// Create web response.
    pub fn into_response(self, response: Response) -> WebResponse {
        WebResponse::new(response, self.request)
    }

    #[inline]
    #[must_use]
    /// Get reference to original request.
    pub fn request(&self) -> &HttpRequest {
        &self.request
    }

    #[inline]
    /// Get reference to response.
    pub fn response(&self) -> &Response<Body> {
        &self.response
    }

    #[inline]
    /// Get mutable reference to response.
    pub fn response_mut(&mut self) -> &mut Response<Body> {
        &mut self.response
    }

    #[inline]
    /// Get the response status code.
    pub fn status(&self) -> StatusCode {
        self.response.status()
    }

    #[inline]
    /// Returns response's headers.
    pub fn headers(&self) -> &HeaderMap {
        self.response.headers()
    }

    #[inline]
    /// Returns mutable response's headers.
    pub fn headers_mut(&mut self) -> &mut HeaderMap {
        self.response.headers_mut()
    }

    #[must_use]
    /// Extract response body.
    pub fn take_body(&mut self) -> ResponseBody<Body> {
        self.response.take_body()
    }

    #[must_use]
    /// Set a new body.
    pub fn map_body<F>(self, f: F) -> WebResponse
    where
        F: FnOnce(&mut ResponseHead, ResponseBody<Body>) -> ResponseBody<Body>,
    {
        let response = self.response.map_body(f);

        WebResponse {
            response,
            request: self.request,
        }
    }

    /// Destruct response into parts.
    pub fn into_parts(self) -> (Response<Body>, HttpRequest) {
        (self.response, self.request)
    }
}

impl From<WebResponse> for Response<Body> {
    fn from(res: WebResponse) -> Response<Body> {
        res.response
    }
}

impl fmt::Debug for WebResponse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let res = writeln!(
            f,
            "\nWebResponse {:?} {}{}",
            self.response.head().version,
            self.response.head().status,
            self.response.head().reason.unwrap_or(""),
        );
        let _ = writeln!(f, "  headers:");
        for (key, val) in &self.response.head().headers {
            let _ = writeln!(f, "    {key:?}: {val:?}");
        }
        let _ = writeln!(f, "  body: {:?}", self.response.body().size());
        res
    }
}

#[cfg(test)]
mod tests {
    use crate::http::{self, StatusCode};
    use crate::web::{HttpResponse, test::TestRequest};

    #[test]
    fn test_response() {
        let res = TestRequest::default().to_srv_response(HttpResponse::Ok().build());
        let res = res.into_response(HttpResponse::BadRequest().build());
        assert_eq!(res.response().status(), StatusCode::BAD_REQUEST);

        let err = http::error::PayloadError::Overflow;
        let res = res.error_response::<()>(&(), err);
        assert_eq!(res.response().status(), StatusCode::PAYLOAD_TOO_LARGE);
    }
}