1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 09:49:29 +00:00

remove json and url encoded form support from -http (#2148)

This commit is contained in:
Rob Ede 2021-04-12 10:30:28 +01:00 committed by GitHub
parent 44c55dd036
commit 981c54432c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 137 additions and 258 deletions

View file

@ -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

View file

@ -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

View file

@ -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" }

View file

@ -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,

View file

@ -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)
);
}

View file

@ -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 {

View file

@ -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();

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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 {

View file

@ -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();

View file

@ -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(),

View file

@ -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));
}
}

View file

@ -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);
}