1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-05-19 16:58:14 +00:00
actix-web/actix-web/src/response/responder.rs

350 lines
12 KiB
Rust
Raw Normal View History

2021-06-17 16:57:58 +00:00
use std::borrow::Cow;
2019-11-20 17:33:22 +00:00
2021-01-15 02:11:10 +00:00
use actix_http::{
2021-12-04 19:40:47 +00:00
body::{BoxBody, EitherBody, MessageBody},
header::TryIntoHeaderPair,
StatusCode,
};
2019-03-02 06:51:32 +00:00
use bytes::{Bytes, BytesMut};
use super::CustomizeResponder;
2022-02-22 07:05:28 +00:00
use crate::{Error, HttpRequest, HttpResponse};
2021-01-15 02:11:10 +00:00
/// Trait implemented by types that can be converted to an HTTP response.
2019-03-02 06:51:32 +00:00
///
2022-02-22 07:05:28 +00:00
/// Any types that implement this trait can be used in the return type of a handler. Since handlers
/// will only have one return type, it is idiomatic to use opaque return types `-> impl Responder`.
///
/// # Implementations
/// It is often not required to implement `Responder` for your own types due to a broad base of
/// built-in implementations:
/// - `HttpResponse` and `HttpResponseBuilder`
/// - `Option<R>` where `R: Responder`
/// - `Result<R, E>` where `R: Responder` and [`E: ResponseError`](crate::ResponseError)
2023-02-12 02:47:42 +00:00
/// - `(R, StatusCode)` where `R: Responder`
2022-02-22 07:05:28 +00:00
/// - `&'static str`, `String`, `&'_ String`, `Cow<'_, str>`, [`ByteString`](bytestring::ByteString)
/// - `&'static [u8]`, `Vec<u8>`, `Bytes`, `BytesMut`
/// - [`Json<T>`](crate::web::Json) and [`Form<T>`](crate::web::Form) where `T: Serialize`
/// - [`Either<L, R>`](crate::web::Either) where `L: Serialize` and `R: Serialize`
/// - [`CustomizeResponder<R>`]
/// - [`actix_files::NamedFile`](https://docs.rs/actix-files/latest/actix_files/struct.NamedFile.html)
/// - [Experimental responders from `actix-web-lab`](https://docs.rs/actix-web-lab/latest/actix_web_lab/respond/index.html)
/// - Third party integrations may also have implemented `Responder` where appropriate. For example,
/// HTML templating engines.
///
/// # Customizing Responder Output
/// Calling [`.customize()`](Responder::customize) on any responder type will wrap it in a
/// [`CustomizeResponder`] capable of overriding various parts of the response such as the status
/// code and header map.
2019-03-02 06:51:32 +00:00
pub trait Responder {
2021-12-04 19:40:47 +00:00
type Body: MessageBody + 'static;
/// Convert self to `HttpResponse`.
2021-12-04 19:40:47 +00:00
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body>;
/// Wraps responder to allow alteration of its response.
///
2022-02-22 07:05:28 +00:00
/// See [`CustomizeResponder`] docs for more details on its capabilities.
///
/// # Examples
/// ```
/// use actix_web::{Responder, http::StatusCode, test::TestRequest};
///
/// let responder = "Hello world!"
/// .customize()
/// .with_status(StatusCode::BAD_REQUEST)
/// .insert_header(("x-hello", "world"));
///
/// let request = TestRequest::default().to_http_request();
/// let response = responder.respond_to(&request);
/// assert_eq!(response.status(), StatusCode::BAD_REQUEST);
/// assert_eq!(response.headers().get("x-hello").unwrap(), "world");
/// ```
#[inline]
fn customize(self) -> CustomizeResponder<Self>
where
Self: Sized,
{
CustomizeResponder::new(self)
}
2022-02-08 16:53:09 +00:00
#[doc(hidden)]
#[deprecated(since = "4.0.0", note = "Prefer `.customize().with_status(header)`.")]
fn with_status(self, status: StatusCode) -> CustomizeResponder<Self>
where
Self: Sized,
{
self.customize().with_status(status)
}
#[doc(hidden)]
#[deprecated(since = "4.0.0", note = "Prefer `.customize().insert_header(header)`.")]
fn with_header(self, header: impl TryIntoHeaderPair) -> CustomizeResponder<Self>
where
Self: Sized,
{
self.customize().insert_header(header)
}
2019-03-02 06:51:32 +00:00
}
2021-12-04 19:40:47 +00:00
impl Responder for actix_http::Response<BoxBody> {
type Body = BoxBody;
#[inline]
2021-12-04 19:40:47 +00:00
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::from(self)
}
}
impl Responder for actix_http::ResponseBuilder {
2021-12-04 19:40:47 +00:00
type Body = BoxBody;
#[inline]
2021-12-04 19:40:47 +00:00
fn respond_to(mut self, req: &HttpRequest) -> HttpResponse<Self::Body> {
self.finish().map_into_boxed_body().respond_to(req)
}
}
2022-02-22 07:05:28 +00:00
impl<R: Responder> Responder for Option<R> {
type Body = EitherBody<R::Body>;
2021-12-04 19:40:47 +00:00
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
2019-03-02 06:51:32 +00:00
match self {
2021-12-04 19:40:47 +00:00
Some(val) => val.respond_to(req).map_into_left_body(),
None => HttpResponse::new(StatusCode::NOT_FOUND).map_into_right_body(),
2019-03-02 06:51:32 +00:00
}
}
}
2022-02-22 07:05:28 +00:00
impl<R, E> Responder for Result<R, E>
2019-03-02 06:51:32 +00:00
where
2022-02-22 07:05:28 +00:00
R: Responder,
2019-03-02 06:51:32 +00:00
E: Into<Error>,
{
2022-02-22 07:05:28 +00:00
type Body = EitherBody<R::Body>;
2021-12-04 19:40:47 +00:00
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
2019-03-02 06:51:32 +00:00
match self {
2021-12-04 19:40:47 +00:00
Ok(val) => val.respond_to(req).map_into_left_body(),
Err(err) => HttpResponse::from_error(err.into()).map_into_right_body(),
2019-03-02 06:51:32 +00:00
}
}
}
2022-02-22 07:05:28 +00:00
impl<R: Responder> Responder for (R, StatusCode) {
type Body = R::Body;
2021-12-04 19:40:47 +00:00
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
let mut res = self.0.respond_to(req);
*res.status_mut() = self.1;
res
}
}
2021-12-04 19:40:47 +00:00
macro_rules! impl_responder_by_forward_into_base_response {
($res:ty, $body:ty) => {
impl Responder for $res {
2021-12-04 19:40:47 +00:00
type Body = $body;
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
let res: actix_http::Response<_> = self.into();
res.into()
}
}
};
2019-03-02 06:51:32 +00:00
2021-12-04 19:40:47 +00:00
($res:ty) => {
impl_responder_by_forward_into_base_response!($res, $res);
};
}
2019-03-02 06:51:32 +00:00
2021-12-04 19:40:47 +00:00
impl_responder_by_forward_into_base_response!(&'static [u8]);
2022-02-04 20:37:33 +00:00
impl_responder_by_forward_into_base_response!(Vec<u8>);
2021-12-04 19:40:47 +00:00
impl_responder_by_forward_into_base_response!(Bytes);
impl_responder_by_forward_into_base_response!(BytesMut);
2019-03-02 06:51:32 +00:00
2021-12-04 19:40:47 +00:00
impl_responder_by_forward_into_base_response!(&'static str);
impl_responder_by_forward_into_base_response!(String);
2022-02-22 07:05:28 +00:00
impl_responder_by_forward_into_base_response!(bytestring::ByteString);
2019-03-02 06:51:32 +00:00
2021-12-04 19:40:47 +00:00
macro_rules! impl_into_string_responder {
($res:ty) => {
impl Responder for $res {
type Body = String;
2021-12-04 19:40:47 +00:00
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
let string: String = self.into();
let res: actix_http::Response<_> = string.into();
res.into()
}
}
};
}
2021-12-04 19:40:47 +00:00
impl_into_string_responder!(&'_ String);
impl_into_string_responder!(Cow<'_, str>);
2019-03-02 06:51:32 +00:00
#[cfg(test)]
2019-03-17 16:52:41 +00:00
pub(crate) mod tests {
2023-07-17 01:38:12 +00:00
use actix_http::body::to_bytes;
use actix_service::Service;
2019-03-13 05:57:09 +00:00
use super::*;
2021-12-04 19:40:47 +00:00
use crate::{
error,
http::header::{HeaderValue, CONTENT_TYPE},
2021-12-04 19:40:47 +00:00
test::{assert_body_eq, init_service, TestRequest},
web, App,
};
2019-11-26 05:25:50 +00:00
#[actix_rt::test]
async fn test_option_responder() {
let srv = init_service(
2019-11-26 05:25:50 +00:00
App::new()
2021-02-11 23:03:17 +00:00
.service(web::resource("/none").to(|| async { Option::<&'static str>::None }))
2019-11-26 05:25:50 +00:00
.service(web::resource("/some").to(|| async { Some("some") })),
)
.await;
let req = TestRequest::with_uri("/none").to_request();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/some").to_request();
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
2021-12-04 19:40:47 +00:00
assert_body_eq!(resp, b"some");
}
2019-11-26 05:25:50 +00:00
#[actix_rt::test]
async fn test_responder() {
let req = TestRequest::default().to_http_request();
2021-12-04 19:40:47 +00:00
let res = "test".respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
2019-11-26 05:25:50 +00:00
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
2019-11-26 05:25:50 +00:00
HeaderValue::from_static("text/plain; charset=utf-8")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
2021-12-04 19:40:47 +00:00
let res = b"test".respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
2019-11-26 05:25:50 +00:00
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
2019-11-26 05:25:50 +00:00
HeaderValue::from_static("application/octet-stream")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
2021-12-04 19:40:47 +00:00
let res = "test".to_string().respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
2019-11-26 05:25:50 +00:00
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
2019-11-26 05:25:50 +00:00
HeaderValue::from_static("text/plain; charset=utf-8")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
2021-12-04 19:40:47 +00:00
let res = (&"test".to_string()).respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
2019-11-26 05:25:50 +00:00
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
2019-11-26 05:25:50 +00:00
HeaderValue::from_static("text/plain; charset=utf-8")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
let s = String::from("test");
2021-12-04 19:40:47 +00:00
let res = Cow::Borrowed(s.as_str()).respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2021-12-04 19:40:47 +00:00
let res = Cow::<'_, str>::Owned(s).respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2021-12-04 19:40:47 +00:00
let res = Cow::Borrowed("test").respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
2019-11-26 05:25:50 +00:00
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
2021-12-04 19:40:47 +00:00
let res = Bytes::from_static(b"test").respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
2019-11-26 05:25:50 +00:00
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
2019-11-26 05:25:50 +00:00
HeaderValue::from_static("application/octet-stream")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
2021-12-04 19:40:47 +00:00
let res = BytesMut::from(b"test".as_ref()).respond_to(&req);
assert_eq!(res.status(), StatusCode::OK);
2019-11-26 05:25:50 +00:00
assert_eq!(
2021-12-04 19:40:47 +00:00
res.headers().get(CONTENT_TYPE).unwrap(),
2019-11-26 05:25:50 +00:00
HeaderValue::from_static("application/octet-stream")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(res.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
// InternalError
2021-12-04 19:40:47 +00:00
let res = error::InternalError::new("err", StatusCode::BAD_REQUEST).respond_to(&req);
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
2019-03-13 05:57:09 +00:00
}
2019-11-26 05:25:50 +00:00
#[actix_rt::test]
async fn test_result_responder() {
let req = TestRequest::default().to_http_request();
2019-11-20 17:33:22 +00:00
2019-11-26 05:25:50 +00:00
// Result<I, E>
let resp = Ok::<_, Error>("test".to_string()).respond_to(&req);
2019-11-26 05:25:50 +00:00
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(
resp.headers().get(CONTENT_TYPE).unwrap(),
HeaderValue::from_static("text/plain; charset=utf-8")
);
2021-12-04 19:40:47 +00:00
assert_eq!(
to_bytes(resp.into_body()).await.unwrap(),
Bytes::from_static(b"test"),
);
2019-11-26 05:25:50 +00:00
2021-02-11 23:03:17 +00:00
let res = Err::<String, _>(error::InternalError::new("err", StatusCode::BAD_REQUEST))
.respond_to(&req);
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
}