3 ms·
You can just have `Request` as a parameter and do those edge cases yourself. Or write your own Extractor which would handle that pretty easily. I'm not sure of
by elysian-breeze 4y ago
You can just have `Request` as a parameter and do those edge cases yourself. Or write your own Extractor which would handle that pretty easily.
I'm not sure of your use case where clients can send any format they want and the HTTP server is supposed to know and handle any format automatically (form, json, querystring, etc), but seems more like a legacy edge case than something you would do building a server from scratch.
Something like this (completely untested) but pretty straight forward to handle your usecase.
#[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub struct FormOrJson<T>(pub T);
#[async_trait]
impl<T, S, B> FromRequest<S, B> for FormOrJson<T>
where
T: DeserializeOwned,
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = InvalidFormOrJson;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
if json_content_type(req.headers()) {
let bytes = Bytes::from_request(req, state).await?;
let deserializer = &mut serde_json::Deserializer::from_slice(&bytes);
let value = match serde_path_to_error::deserialize(deserializer) {
Ok(value) => value,
Err(err) => {
let rejection = match err.inner().classify() {
serde_json::error::Category::Data => JsonDataError::from_err(err).into(),
serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
JsonSyntaxError::from_err(err).into()
}
serde_json::error::Category::Io => {
if cfg!(debug_assertions) {
// we don't use `serde_json::from_reader` and instead always buffer
// bodies first, so we shouldn't encounter any IO errors
unreachable!()
} else {
JsonSyntaxError::from_err(err).into()
}
}
};
return Err(rejection);
}
};
Ok(FormOrJson(value))
} else if has_content_type(req, &mime::APPLICATION_WWW_FORM_URLENCODED) {
let bytes = Bytes::from_request(req).await?;
let value = serde_urlencoded::from_bytes(&bytes)
.map_err(FailedToDeserializeQueryString::__private_new::<(), _>)?;
Ok(FormOrJson(value))
} else {
Err(InvalidFormOrJson.into())
}
}
}
- zemo 4y ago> I'm not sure of your use case where clients can send any format they want and the HTTP server is supposed to know and handle any format automatically (form, json, querystring, etc) Any server that has clients that aren't fully under your control that you can't force-update, where the clients today and the clients yesterday encode their requests differently. I mean, it's the entire purpose of the `Content-Type` header. The whole concept is that the server has a set of encodings that it can understand, and the client can pick between them. This isn't a new idea. Here it is in RFC 2068, from 1997: https://www.rfc-editor.org/rfc/rfc2068#page-116 https://www.rfc-editor.org/rfc/rfc2068#page-116 These concepts have existed for decades. In your comment, you make an implicit assumption: the assumption is that the person that writes the server is in control of the client. When we make tools that make it easier to construct software that assumes the server operator is in control of the client but do nothing to make it easier to build software in which the server operator is not in control of the client, we are making a political choice to place power in the hands of server operators at the expense of end users. That's not a political choice that I'm comfortable with.