mirror of
https://github.com/fafhrd91/actix-web
synced 2024-12-18 01:43:58 +01:00
remove json and url encoded form support from -http (#2148)
This commit is contained in:
parent
44c55dd036
commit
981c54432c
@ -4,7 +4,12 @@
|
||||
### Added
|
||||
* `HttpResponse` and `HttpResponseBuilder` structs. [#2065]
|
||||
|
||||
### Changed
|
||||
* Most error types are now marked `#[non_exhaustive]`. [#2148]
|
||||
|
||||
[#2065]: https://github.com/actix/actix-web/pull/2065
|
||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||
|
||||
|
||||
## 4.0.0-beta.5 - 2021-04-02
|
||||
### Added
|
||||
|
@ -6,8 +6,13 @@
|
||||
* Top-level `cookies` mod (re-export). [#2065]
|
||||
* `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065]
|
||||
* `impl ResponseError for CookieParseError`. [#2065]
|
||||
* Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148]
|
||||
* `ResponseBuilder::json`. [#2148]
|
||||
* `ResponseBuilder::{set_header, header}`. [#2148]
|
||||
* `impl From<serde_json::Value> for Body`. [#2148]
|
||||
|
||||
[#2065]: https://github.com/actix/actix-web/pull/2065
|
||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||
|
||||
|
||||
## 3.0.0-beta.5 - 2021-04-02
|
||||
|
@ -68,8 +68,6 @@ pin-project-lite = "0.2"
|
||||
rand = "0.8"
|
||||
regex = "1.3"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.7"
|
||||
sha-1 = "0.9"
|
||||
smallvec = "1.6"
|
||||
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
||||
@ -89,6 +87,7 @@ criterion = "0.3"
|
||||
env_logger = "0.8"
|
||||
rcgen = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tls-openssl = { version = "0.10", package = "openssl" }
|
||||
tls-rustls = { version = "0.19", package = "rustls" }
|
||||
|
||||
|
@ -132,12 +132,6 @@ impl From<BytesMut> for Body {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Value> for Body {
|
||||
fn from(v: serde_json::Value) -> Body {
|
||||
Body::Bytes(v.to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<SizedStream<S>> for Body
|
||||
where
|
||||
S: Stream<Item = Result<Bytes, Error>> + Unpin + 'static,
|
||||
|
@ -174,13 +174,15 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_serde_json() {
|
||||
use serde_json::json;
|
||||
use serde_json::{json, Value};
|
||||
assert_eq!(
|
||||
Body::from(serde_json::Value::String("test".into())).size(),
|
||||
Body::from(serde_json::to_vec(&Value::String("test".to_owned())).unwrap())
|
||||
.size(),
|
||||
BodySize::Sized(6)
|
||||
);
|
||||
assert_eq!(
|
||||
Body::from(json!({"test-key":"test-value"})).size(),
|
||||
Body::from(serde_json::to_vec(&json!({"test-key":"test-value"})).unwrap())
|
||||
.size(),
|
||||
BodySize::Sized(25)
|
||||
);
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
//! Error and Result module
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::io::Write;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
use std::{fmt, io, result};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt,
|
||||
io::{self, Write as _},
|
||||
str::Utf8Error,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use derive_more::{Display, From};
|
||||
use derive_more::{Display, Error, From};
|
||||
use http::uri::InvalidUri;
|
||||
use http::{header, Error as HttpError, StatusCode};
|
||||
use serde::de::value::Error as DeError;
|
||||
use serde_json::error::Error as JsonError;
|
||||
use serde_urlencoded::ser::Error as FormError;
|
||||
|
||||
use crate::{body::Body, helpers::Writer, Response, ResponseBuilder};
|
||||
|
||||
@ -22,7 +22,7 @@ use crate::{body::Body, helpers::Writer, Response, ResponseBuilder};
|
||||
/// This typedef is generally used to avoid writing out
|
||||
/// `actix_http::error::Error` directly and is otherwise a direct mapping to
|
||||
/// `Result`.
|
||||
pub type Result<T, E = Error> = result::Result<T, E>;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// General purpose actix web error.
|
||||
///
|
||||
@ -147,14 +147,8 @@ struct UnitError;
|
||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`UnitError`].
|
||||
impl ResponseError for UnitError {}
|
||||
|
||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`JsonError`].
|
||||
impl ResponseError for JsonError {}
|
||||
|
||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`FormError`].
|
||||
impl ResponseError for FormError {}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`actix_tls::accept::openssl::SslError`].
|
||||
#[cfg(feature = "openssl")]
|
||||
impl ResponseError for actix_tls::accept::openssl::SslError {}
|
||||
|
||||
/// Returns [`StatusCode::BAD_REQUEST`] for [`DeError`].
|
||||
@ -421,18 +415,17 @@ pub enum DispatchError {
|
||||
}
|
||||
|
||||
/// A set of error that can occur during parsing content type
|
||||
#[derive(PartialEq, Debug, Display)]
|
||||
#[derive(Debug, PartialEq, Display, Error)]
|
||||
pub enum ContentTypeError {
|
||||
/// Can not parse content type
|
||||
#[display(fmt = "Can not parse content type")]
|
||||
ParseError,
|
||||
|
||||
/// Unknown content encoding
|
||||
#[display(fmt = "Unknown content encoding")]
|
||||
UnknownEncoding,
|
||||
}
|
||||
|
||||
impl std::error::Error for ContentTypeError {}
|
||||
|
||||
/// Return `BadRequest` for `ContentTypeError`
|
||||
impl ResponseError for ContentTypeError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
convert::TryInto,
|
||||
fmt,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
@ -12,17 +11,13 @@ use std::{
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures_core::Stream;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
body::{Body, BodyStream, MessageBody, ResponseBody},
|
||||
error::Error,
|
||||
extensions::Extensions,
|
||||
header::{IntoHeaderPair, IntoHeaderValue},
|
||||
http::{
|
||||
header::{self, HeaderName},
|
||||
Error as HttpError, HeaderMap, StatusCode,
|
||||
},
|
||||
http::{header, Error as HttpError, HeaderMap, StatusCode},
|
||||
message::{BoxedResponseHead, ConnectionType, ResponseHead},
|
||||
};
|
||||
|
||||
@ -335,54 +330,6 @@ impl ResponseBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Replaced with [`Self::insert_header()`].
|
||||
#[deprecated(
|
||||
since = "4.0.0",
|
||||
note = "Replaced with `insert_header((key, value))`. Will be removed in v5."
|
||||
)]
|
||||
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||
where
|
||||
K: TryInto<HeaderName>,
|
||||
K::Error: Into<HttpError>,
|
||||
V: IntoHeaderValue,
|
||||
{
|
||||
if self.err.is_some() {
|
||||
return self;
|
||||
}
|
||||
|
||||
match (key.try_into(), value.try_into_value()) {
|
||||
(Ok(name), Ok(value)) => return self.insert_header((name, value)),
|
||||
(Err(err), _) => self.err = Some(err.into()),
|
||||
(_, Err(err)) => self.err = Some(err.into()),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Replaced with [`Self::append_header()`].
|
||||
#[deprecated(
|
||||
since = "4.0.0",
|
||||
note = "Replaced with `append_header((key, value))`. Will be removed in v5."
|
||||
)]
|
||||
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||
where
|
||||
K: TryInto<HeaderName>,
|
||||
K::Error: Into<HttpError>,
|
||||
V: IntoHeaderValue,
|
||||
{
|
||||
if self.err.is_some() {
|
||||
return self;
|
||||
}
|
||||
|
||||
match (key.try_into(), value.try_into_value()) {
|
||||
(Ok(name), Ok(value)) => return self.append_header((name, value)),
|
||||
(Err(err), _) => self.err = Some(err.into()),
|
||||
(_, Err(err)) => self.err = Some(err.into()),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the custom reason for the response.
|
||||
#[inline]
|
||||
pub fn reason(&mut self, reason: &'static str) -> &mut Self {
|
||||
@ -456,32 +403,6 @@ impl ResponseBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// This method calls provided closure with builder reference if value is `true`.
|
||||
#[doc(hidden)]
|
||||
#[deprecated = "Use an if statement."]
|
||||
pub fn if_true<F>(&mut self, value: bool, f: F) -> &mut Self
|
||||
where
|
||||
F: FnOnce(&mut ResponseBuilder),
|
||||
{
|
||||
if value {
|
||||
f(self);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// This method calls provided closure with builder reference if value is `Some`.
|
||||
#[doc(hidden)]
|
||||
#[deprecated = "Use an if-let construction."]
|
||||
pub fn if_some<T, F>(&mut self, value: Option<T>, f: F) -> &mut Self
|
||||
where
|
||||
F: FnOnce(T, &mut ResponseBuilder),
|
||||
{
|
||||
if let Some(val) = value {
|
||||
f(val, self);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Responses extensions
|
||||
#[inline]
|
||||
pub fn extensions(&self) -> Ref<'_, Extensions> {
|
||||
@ -496,10 +417,10 @@ impl ResponseBuilder {
|
||||
head.extensions.borrow_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Set a body and generate `Response`.
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn body<B: Into<Body>>(&mut self, body: B) -> Response {
|
||||
self.message_body(body.into())
|
||||
}
|
||||
@ -521,10 +442,10 @@ impl ResponseBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Set a streaming body and generate `Response`.
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn streaming<S, E>(&mut self, stream: S) -> Response
|
||||
where
|
||||
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
|
||||
@ -533,32 +454,10 @@ impl ResponseBuilder {
|
||||
self.body(Body::from_message(BodyStream::new(stream)))
|
||||
}
|
||||
|
||||
/// Set a json body and generate `Response`
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
pub fn json(&mut self, value: impl Serialize) -> Response {
|
||||
match serde_json::to_string(&value) {
|
||||
Ok(body) => {
|
||||
let contains = if let Some(parts) = parts(&mut self.head, &self.err) {
|
||||
parts.headers.contains_key(header::CONTENT_TYPE)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if !contains {
|
||||
self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
|
||||
}
|
||||
|
||||
self.body(Body::from(body))
|
||||
}
|
||||
Err(e) => Error::from(e).into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Set an empty body and generate `Response`
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn finish(&mut self) -> Response {
|
||||
self.body(Body::Empty)
|
||||
}
|
||||
@ -706,11 +605,9 @@ impl From<BytesMut> for Response {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
use crate::body::Body;
|
||||
use crate::http::header::{HeaderValue, CONTENT_TYPE, COOKIE};
|
||||
use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE, COOKIE};
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
@ -754,38 +651,6 @@ mod tests {
|
||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json() {
|
||||
let resp = Response::Ok().json(vec!["v1", "v2", "v3"]);
|
||||
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
|
||||
assert_eq!(ct, HeaderValue::from_static("application/json"));
|
||||
assert_eq!(resp.body().get_ref(), b"[\"v1\",\"v2\",\"v3\"]");
|
||||
|
||||
let resp = Response::Ok().json(&["v1", "v2", "v3"]);
|
||||
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
|
||||
assert_eq!(ct, HeaderValue::from_static("application/json"));
|
||||
assert_eq!(resp.body().get_ref(), b"[\"v1\",\"v2\",\"v3\"]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_ct() {
|
||||
let resp = Response::build(StatusCode::OK)
|
||||
.insert_header((CONTENT_TYPE, "text/json"))
|
||||
.json(&vec!["v1", "v2", "v3"]);
|
||||
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
|
||||
assert_eq!(ct, HeaderValue::from_static("text/json"));
|
||||
assert_eq!(resp.body().get_ref(), b"[\"v1\",\"v2\",\"v3\"]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_json_in_body() {
|
||||
use serde_json::json;
|
||||
|
||||
let resp =
|
||||
Response::build(StatusCode::OK).body(json!({"test-key":"test-value"}));
|
||||
assert_eq!(resp.body().get_ref(), br#"{"test-key":"test-value"}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_response() {
|
||||
let resp: Response = "test".into();
|
||||
|
@ -1,6 +1,10 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2021-xx-xx
|
||||
### Removed
|
||||
* Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148]
|
||||
|
||||
[#2148]: https://github.com/actix/actix-web/pull/2148
|
||||
|
||||
|
||||
## 3.0.0-beta.4 - 2021-04-02
|
||||
|
@ -311,34 +311,6 @@ impl ClientRequest {
|
||||
self
|
||||
}
|
||||
|
||||
/// This method calls provided closure with builder reference if value is `true`.
|
||||
#[doc(hidden)]
|
||||
#[deprecated = "Use an if statement."]
|
||||
pub fn if_true<F>(self, value: bool, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(ClientRequest) -> ClientRequest,
|
||||
{
|
||||
if value {
|
||||
f(self)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// This method calls provided closure with builder reference if value is `Some`.
|
||||
#[doc(hidden)]
|
||||
#[deprecated = "Use an if-let construction."]
|
||||
pub fn if_some<T, F>(self, value: Option<T>, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(T, ClientRequest) -> ClientRequest,
|
||||
{
|
||||
if let Some(val) = value {
|
||||
f(val, self)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the query part of the request
|
||||
pub fn query<T: Serialize>(
|
||||
mut self,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
future::Future,
|
||||
net,
|
||||
io, net,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
task::{Context, Poll},
|
||||
@ -209,7 +209,8 @@ impl RequestSender {
|
||||
) -> SendClientRequest {
|
||||
let body = match serde_json::to_string(value) {
|
||||
Ok(body) => body,
|
||||
Err(e) => return Error::from(e).into(),
|
||||
// TODO: own error type
|
||||
Err(e) => return Error::from(io::Error::new(io::ErrorKind::Other, e)).into(),
|
||||
};
|
||||
|
||||
if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") {
|
||||
@ -235,7 +236,8 @@ impl RequestSender {
|
||||
) -> SendClientRequest {
|
||||
let body = match serde_urlencoded::to_string(value) {
|
||||
Ok(body) => body,
|
||||
Err(e) => return Error::from(e).into(),
|
||||
// TODO: own error type
|
||||
Err(e) => return Error::from(io::Error::new(io::ErrorKind::Other, e)).into(),
|
||||
};
|
||||
|
||||
// set content-type
|
||||
|
64
src/error.rs
64
src/error.rs
@ -3,12 +3,15 @@
|
||||
pub use actix_http::error::*;
|
||||
use derive_more::{Display, Error, From};
|
||||
use serde_json::error::Error as JsonError;
|
||||
use serde_urlencoded::de::Error as FormDeError;
|
||||
use serde_urlencoded::ser::Error as FormError;
|
||||
use url::ParseError as UrlParseError;
|
||||
|
||||
use crate::http::StatusCode;
|
||||
|
||||
/// Errors which can occur when attempting to generate resource uri.
|
||||
#[derive(Debug, PartialEq, Display, From)]
|
||||
#[derive(Debug, PartialEq, Display, Error, From)]
|
||||
#[non_exhaustive]
|
||||
pub enum UrlGenerationError {
|
||||
/// Resource not found
|
||||
#[display(fmt = "Resource not found")]
|
||||
@ -23,13 +26,12 @@ pub enum UrlGenerationError {
|
||||
ParseError(UrlParseError),
|
||||
}
|
||||
|
||||
impl std::error::Error for UrlGenerationError {}
|
||||
|
||||
/// `InternalServerError` for `UrlGeneratorError`
|
||||
impl ResponseError for UrlGenerationError {}
|
||||
|
||||
/// A set of errors that can occur during parsing urlencoded payloads
|
||||
#[derive(Debug, Display, Error, From)]
|
||||
#[non_exhaustive]
|
||||
pub enum UrlencodedError {
|
||||
/// Can not decode chunked transfer encoding.
|
||||
#[display(fmt = "Can not decode chunked transfer encoding.")]
|
||||
@ -52,8 +54,16 @@ pub enum UrlencodedError {
|
||||
ContentType,
|
||||
|
||||
/// Parse error.
|
||||
#[display(fmt = "Parse error.")]
|
||||
Parse,
|
||||
#[display(fmt = "Parse error: {}.", _0)]
|
||||
Parse(FormDeError),
|
||||
|
||||
/// Encoding error.
|
||||
#[display(fmt = "Encoding error.")]
|
||||
Encoding,
|
||||
|
||||
/// Serialize error.
|
||||
#[display(fmt = "Serialize error: {}.", _0)]
|
||||
Serialize(FormError),
|
||||
|
||||
/// Payload error.
|
||||
#[display(fmt = "Error that occur during reading payload: {}.", _0)]
|
||||
@ -63,52 +73,66 @@ pub enum UrlencodedError {
|
||||
/// Return `BadRequest` for `UrlencodedError`
|
||||
impl ResponseError for UrlencodedError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
UrlencodedError::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
UrlencodedError::UnknownLength => StatusCode::LENGTH_REQUIRED,
|
||||
match self {
|
||||
Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
Self::UnknownLength => StatusCode::LENGTH_REQUIRED,
|
||||
Self::Payload(err) => err.status_code(),
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of errors that can occur during parsing json payloads
|
||||
#[derive(Debug, Display, From)]
|
||||
#[derive(Debug, Display, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum JsonPayloadError {
|
||||
/// Payload size is bigger than allowed. (default: 32kB)
|
||||
#[display(fmt = "Json payload size is bigger than allowed")]
|
||||
Overflow,
|
||||
|
||||
/// Content type error
|
||||
#[display(fmt = "Content type error")]
|
||||
ContentType,
|
||||
|
||||
/// Deserialize error
|
||||
#[display(fmt = "Json deserialize error: {}", _0)]
|
||||
Deserialize(JsonError),
|
||||
|
||||
/// Serialize error
|
||||
#[display(fmt = "Json serialize error: {}", _0)]
|
||||
Serialize(JsonError),
|
||||
|
||||
/// Payload error
|
||||
#[display(fmt = "Error that occur during reading payload: {}", _0)]
|
||||
Payload(PayloadError),
|
||||
}
|
||||
|
||||
impl std::error::Error for JsonPayloadError {}
|
||||
impl From<PayloadError> for JsonPayloadError {
|
||||
fn from(err: PayloadError) -> Self {
|
||||
Self::Payload(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for JsonPayloadError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
JsonPayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
match self {
|
||||
Self::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
Self::Serialize(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::Payload(err) => err.status_code(),
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of errors that can occur during parsing request paths
|
||||
#[derive(Debug, Display, From)]
|
||||
#[derive(Debug, Display, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum PathError {
|
||||
/// Deserialize error
|
||||
#[display(fmt = "Path deserialize error: {}", _0)]
|
||||
Deserialize(serde::de::value::Error),
|
||||
}
|
||||
|
||||
impl std::error::Error for PathError {}
|
||||
|
||||
/// Return `BadRequest` for `PathError`
|
||||
impl ResponseError for PathError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
@ -118,6 +142,7 @@ impl ResponseError for PathError {
|
||||
|
||||
/// A set of errors that can occur during parsing query strings.
|
||||
#[derive(Debug, Display, Error, From)]
|
||||
#[non_exhaustive]
|
||||
pub enum QueryPayloadError {
|
||||
/// Query deserialize error.
|
||||
#[display(fmt = "Query deserialize error: {}", _0)]
|
||||
@ -132,25 +157,26 @@ impl ResponseError for QueryPayloadError {
|
||||
}
|
||||
|
||||
/// Error type returned when reading body as lines.
|
||||
#[derive(From, Display, Debug)]
|
||||
#[derive(Debug, Display, Error, From)]
|
||||
#[non_exhaustive]
|
||||
pub enum ReadlinesError {
|
||||
/// Error when decoding a line.
|
||||
#[display(fmt = "Encoding error")]
|
||||
/// Payload size is bigger than allowed. (default: 256kB)
|
||||
EncodingError,
|
||||
|
||||
/// Payload error.
|
||||
#[display(fmt = "Error that occur during reading payload: {}", _0)]
|
||||
Payload(PayloadError),
|
||||
|
||||
/// Line limit exceeded.
|
||||
#[display(fmt = "Line limit exceeded")]
|
||||
LimitOverflow,
|
||||
|
||||
/// ContentType error.
|
||||
#[display(fmt = "Content-type error")]
|
||||
ContentTypeError(ContentTypeError),
|
||||
}
|
||||
|
||||
impl std::error::Error for ReadlinesError {}
|
||||
|
||||
/// Return `BadRequest` for `ReadlinesError`
|
||||
impl ResponseError for ReadlinesError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
|
@ -24,7 +24,7 @@ use actix_http::http::header::HeaderValue;
|
||||
#[cfg(feature = "cookies")]
|
||||
use cookie::{Cookie, CookieJar};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::error::{Error, JsonPayloadError};
|
||||
|
||||
/// An HTTP Response
|
||||
pub struct HttpResponse<B = Body> {
|
||||
@ -385,7 +385,10 @@ impl HttpResponseBuilder {
|
||||
}
|
||||
|
||||
/// Replaced with [`Self::insert_header()`].
|
||||
#[deprecated = "Replaced with `insert_header((key, value))`."]
|
||||
#[deprecated(
|
||||
since = "4.0.0",
|
||||
note = "Replaced with `insert_header((key, value))`. Will be removed in v5."
|
||||
)]
|
||||
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||
where
|
||||
K: TryInto<HeaderName>,
|
||||
@ -406,7 +409,10 @@ impl HttpResponseBuilder {
|
||||
}
|
||||
|
||||
/// Replaced with [`Self::append_header()`].
|
||||
#[deprecated = "Replaced with `append_header((key, value))`."]
|
||||
#[deprecated(
|
||||
since = "4.0.0",
|
||||
note = "Replaced with `append_header((key, value))`. Will be removed in v5."
|
||||
)]
|
||||
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
|
||||
where
|
||||
K: TryInto<HeaderName>,
|
||||
@ -572,7 +578,7 @@ impl HttpResponseBuilder {
|
||||
|
||||
/// Set a body and generate `Response`.
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn body<B: Into<Body>>(&mut self, body: B) -> HttpResponse {
|
||||
self.message_body(body.into())
|
||||
@ -580,7 +586,7 @@ impl HttpResponseBuilder {
|
||||
|
||||
/// Set a body and generate `Response`.
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
pub fn message_body<B>(&mut self, body: B) -> HttpResponse<B> {
|
||||
if let Some(err) = self.err.take() {
|
||||
return HttpResponse::from_error(Error::from(err)).into_body();
|
||||
@ -608,7 +614,7 @@ impl HttpResponseBuilder {
|
||||
|
||||
/// Set a streaming body and generate `Response`.
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn streaming<S, E>(&mut self, stream: S) -> HttpResponse
|
||||
where
|
||||
@ -620,7 +626,7 @@ impl HttpResponseBuilder {
|
||||
|
||||
/// Set a json body and generate `Response`
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
pub fn json(&mut self, value: impl Serialize) -> HttpResponse {
|
||||
match serde_json::to_string(&value) {
|
||||
Ok(body) => {
|
||||
@ -636,19 +642,19 @@ impl HttpResponseBuilder {
|
||||
|
||||
self.body(Body::from(body))
|
||||
}
|
||||
Err(e) => HttpResponse::from_error(Error::from(e)),
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an empty body and generate `Response`
|
||||
///
|
||||
/// `ResponseBuilder` can not be used after this call.
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn finish(&mut self) -> HttpResponse {
|
||||
self.body(Body::Empty)
|
||||
}
|
||||
|
||||
/// This method construct new `ResponseBuilder`
|
||||
/// This method construct new `HttpResponseBuilder`
|
||||
pub fn take(&mut self) -> Self {
|
||||
Self {
|
||||
head: self.head.take(),
|
||||
@ -814,32 +820,33 @@ mod http_codes {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use serde_json::json;
|
||||
|
||||
use super::{HttpResponse as Response, HttpResponseBuilder as ResponseBuilder};
|
||||
use super::{HttpResponse, HttpResponseBuilder};
|
||||
use crate::dev::{Body, MessageBody, ResponseBody};
|
||||
use crate::http::header::{self, HeaderValue, CONTENT_TYPE, COOKIE};
|
||||
use crate::http::StatusCode;
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let resp = Response::Ok()
|
||||
let resp = HttpResponse::Ok()
|
||||
.append_header((COOKIE, HeaderValue::from_static("cookie1=value1; ")))
|
||||
.append_header((COOKIE, HeaderValue::from_static("cookie2=value2; ")))
|
||||
.finish();
|
||||
let dbg = format!("{:?}", resp);
|
||||
assert!(dbg.contains("Response"));
|
||||
assert!(dbg.contains("HttpResponse"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_builder() {
|
||||
let resp = Response::Ok().insert_header(("X-TEST", "value")).finish();
|
||||
let resp = HttpResponse::Ok()
|
||||
.insert_header(("X-TEST", "value"))
|
||||
.finish();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upgrade() {
|
||||
let resp = ResponseBuilder::new(StatusCode::OK)
|
||||
let resp = HttpResponseBuilder::new(StatusCode::OK)
|
||||
.upgrade("websocket")
|
||||
.finish();
|
||||
assert!(resp.upgrade());
|
||||
@ -851,13 +858,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_force_close() {
|
||||
let resp = ResponseBuilder::new(StatusCode::OK).force_close().finish();
|
||||
let resp = HttpResponseBuilder::new(StatusCode::OK)
|
||||
.force_close()
|
||||
.finish();
|
||||
assert!(!resp.keep_alive())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_content_type() {
|
||||
let resp = ResponseBuilder::new(StatusCode::OK)
|
||||
let resp = HttpResponseBuilder::new(StatusCode::OK)
|
||||
.content_type("text/plain")
|
||||
.body(Body::Empty);
|
||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
|
||||
@ -878,7 +887,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_json() {
|
||||
let mut resp = Response::Ok().json(vec!["v1", "v2", "v3"]);
|
||||
let mut resp = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]);
|
||||
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
|
||||
assert_eq!(ct, HeaderValue::from_static("application/json"));
|
||||
assert_eq!(
|
||||
@ -886,7 +895,7 @@ mod tests {
|
||||
br#"["v1","v2","v3"]"#
|
||||
);
|
||||
|
||||
let mut resp = Response::Ok().json(&["v1", "v2", "v3"]);
|
||||
let mut resp = HttpResponse::Ok().json(&["v1", "v2", "v3"]);
|
||||
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
|
||||
assert_eq!(ct, HeaderValue::from_static("application/json"));
|
||||
assert_eq!(
|
||||
@ -895,7 +904,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// content type override
|
||||
let mut resp = Response::Ok()
|
||||
let mut resp = HttpResponse::Ok()
|
||||
.insert_header((CONTENT_TYPE, "text/json"))
|
||||
.json(&vec!["v1", "v2", "v3"]);
|
||||
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
|
||||
@ -908,8 +917,10 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_serde_json_in_body() {
|
||||
use serde_json::json;
|
||||
let mut resp = Response::Ok().body(json!({"test-key":"test-value"}));
|
||||
let mut resp = HttpResponse::Ok().body(
|
||||
serde_json::to_vec(&serde_json::json!({ "test-key": "test-value" })).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
read_body(resp.take_body()).await.as_ref(),
|
||||
br#"{"test-key":"test-value"}"#
|
||||
@ -918,7 +929,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn response_builder_header_insert_kv() {
|
||||
let mut res = Response::Ok();
|
||||
let mut res = HttpResponse::Ok();
|
||||
res.insert_header(("Content-Type", "application/octet-stream"));
|
||||
let res = res.finish();
|
||||
|
||||
@ -930,7 +941,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn response_builder_header_insert_typed() {
|
||||
let mut res = Response::Ok();
|
||||
let mut res = HttpResponse::Ok();
|
||||
res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
|
||||
let res = res.finish();
|
||||
|
||||
@ -942,7 +953,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn response_builder_header_append_kv() {
|
||||
let mut res = Response::Ok();
|
||||
let mut res = HttpResponse::Ok();
|
||||
res.append_header(("Content-Type", "application/octet-stream"));
|
||||
res.append_header(("Content-Type", "application/json"));
|
||||
let res = res.finish();
|
||||
@ -955,7 +966,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn response_builder_header_append_typed() {
|
||||
let mut res = Response::Ok();
|
||||
let mut res = HttpResponse::Ok();
|
||||
res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
|
||||
res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
|
||||
let res = res.finish();
|
||||
|
@ -163,7 +163,7 @@ impl<T: Serialize> Responder for Form<T> {
|
||||
Ok(body) => HttpResponse::Ok()
|
||||
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
|
||||
.body(body),
|
||||
Err(err) => HttpResponse::from_error(err.into()),
|
||||
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,14 +346,14 @@ where
|
||||
}
|
||||
|
||||
if encoding == UTF_8 {
|
||||
serde_urlencoded::from_bytes::<T>(&body).map_err(|_| UrlencodedError::Parse)
|
||||
serde_urlencoded::from_bytes::<T>(&body).map_err(UrlencodedError::Parse)
|
||||
} else {
|
||||
let body = encoding
|
||||
.decode_without_bom_handling_and_without_replacement(&body)
|
||||
.map(|s| s.into_owned())
|
||||
.ok_or(UrlencodedError::Parse)?;
|
||||
.ok_or(UrlencodedError::Encoding)?;
|
||||
|
||||
serde_urlencoded::from_str::<T>(&body).map_err(|_| UrlencodedError::Parse)
|
||||
serde_urlencoded::from_str::<T>(&body).map_err(UrlencodedError::Parse)
|
||||
}
|
||||
}
|
||||
.boxed_local(),
|
||||
|
@ -127,7 +127,7 @@ impl<T: Serialize> Responder for Json<T> {
|
||||
Ok(body) => HttpResponse::Ok()
|
||||
.content_type(mime::APPLICATION_JSON)
|
||||
.body(body),
|
||||
Err(err) => HttpResponse::from_error(err.into()),
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -412,7 +412,8 @@ where
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let json = serde_json::from_slice::<T>(&buf)?;
|
||||
let json = serde_json::from_slice::<T>(&buf)
|
||||
.map_err(JsonPayloadError::Deserialize)?;
|
||||
return Poll::Ready(Ok(json));
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ where
|
||||
T: de::DeserializeOwned,
|
||||
{
|
||||
type Error = Error;
|
||||
type Future = Ready<Result<Self, Error>>;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
type Config = PathConfig;
|
||||
|
||||
#[inline]
|
||||
@ -106,17 +106,17 @@ where
|
||||
ready(
|
||||
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
|
||||
.map(Path)
|
||||
.map_err(move |e| {
|
||||
.map_err(move |err| {
|
||||
log::debug!(
|
||||
"Failed during Path extractor deserialization. \
|
||||
Request path: {:?}",
|
||||
req.path()
|
||||
);
|
||||
if let Some(error_handler) = error_handler {
|
||||
let e = PathError::Deserialize(e);
|
||||
let e = PathError::Deserialize(err);
|
||||
(error_handler)(e, req)
|
||||
} else {
|
||||
ErrorNotFound(e)
|
||||
ErrorNotFound(err)
|
||||
}
|
||||
}),
|
||||
)
|
||||
@ -303,7 +303,7 @@ mod tests {
|
||||
let s = Path::<(usize,)>::from_request(&req, &mut pl)
|
||||
.await
|
||||
.unwrap_err();
|
||||
let res = HttpResponse::from_error(s.into());
|
||||
let res = HttpResponse::from_error(s);
|
||||
|
||||
assert_eq!(res.status(), http::StatusCode::CONFLICT);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user