-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Option<T>
guard should fail if T
fails, not be None
#2543
Comments
I think what you're looking for is |
^I don't think in this workaround you even need the But I don't think using I think the only way around it is to take an All this to say, it's actually very hard to get this desired behavior of "Nothing or Exactly this". You basically have to create a new Option-like type that implements Overall it seems that ignoring malformed input is a bad default for |
That's not quite the case. The
Using It would not make sense for Rocket to codify any meaning to any particular semantics for any particular case, failure or otherwise, and as it stands it does not. Just because an empty body is a failure for one type does not mean it would be a failure for another.
There's a much simpler solution that isn't a workaround and takes full advantage of Rocket: implement a custom, generic data guard that works the way you want it to. Let's call it use rocket::request::Request;
use rocket::data::{self, Data, FromData};
enum Maybe<T, E> {
Some(T),
Err(E),
Empty,
}
#[rocket::async_trait]
impl<'r, T: FromData<'r>> FromData<'r> for Maybe<T, <T as FromData<'r>>::Error> {
type Error = std::convert::Infallible;
async fn from_data(req: &'r Request<'_>, mut data: Data<'r>) -> data::Outcome<'r, Self> {
if data.peek().await.is_empty() && data.peek_complete() {
return data::Outcome::Success(Maybe::Empty);
}
match T::from_data(req, data).await {
data::Outcome::Success(v) => data::Outcome::Success(Maybe::Some(v)),
data::Outcome::Failure((s, e)) => data::Outcome::Success(Maybe::Err(e)),
data::Outcome::Forward(data) => data::Outcome::Forward(data)
}
}
} Even this type encodes particular functionality. You may wish to modify it to your liking. Now you can just use it: #[post("/my_route", data = "<body>")]
pub async fn my_route_handler(body: Maybe<Json<Foo>, Error>) {
/* ... */
} |
Ah that's good to know in general, but
It seems we're disagreeing on this point.
I have learned what Option does through experience not intuition, so I wouldn't exactly call it clear, but it is well defined. I'm presenting an alternately clear general semantic: it is None if the body is empty, otherwise it defers to T, mapping Success to Some. Both my proposed semantics and the existing one are sound interpretations of what
I'm just arguing that this change would improve the library. We can call it whatever we want. I'm aware of the ability to make a custom type (seeing as I recommended an implementation of |
You're right. I'm not sure why this wasn't documented. I've pushed a commit (b6b060f) that documents all built-in guards, hopefully clearly.
What I'm trying to say is that there isn't a bug here (besides arguably a doc bug, since this clearly wasn't documented well prior): there isn't something Rocket claims to do but fails to do, or something that Rocket clearly shouldn't be doing or is doing incorrectly. Instead, there's a disagreement about what Rocket should be doing. As far as I understand, the disagreement rests on the what the semantics of
There are a number of problems with the proposed functionality:
If we wanted to change the interpretation of I think points 1) and 3) above make In all, this line of reasoning leads me to believe that As an aside: another equally reasonable interpretation is to have |
Just a thought: if we 1) change |
Yeah this is fantastic! Thanks!
I'm not sure this is a real problem, but I agree it's kinda cool viewing
I'm less worried about this, especially in 0.5 where
This is a really interesting case. Good catch. I'd say I'd be fine with that behavior, and if your T does accept empty bodies, it just shouldn't be used with
I'm not sure this understanding of The https://github.com/search?q=language%3Arust+%22data%3A+Option%3CJson%22+rocket&type=code I doubt most of them are intending to just ingest and ignore malformed data. Granted, that's really not a lot of data but searching github is hard and most web apps aren't publicly available so it's hard to estimate. Anyway, I think at this point it comes down to:
I land pretty firmly on "they should not, and it's not" but ultimately that's a matter of taste. |
FromData
implementation for Option<T>
causes malformed bodies to be silently ignored.Option<T>
guard should fail if T
fails, not be None
Hi, I have a similar issue with form fields.
Do you confirm that the way to go is the following: use rocket::form::FromFormField;
[...The Maybe<T, E> code from https://github.com/rwf2/Rocket/issues/2543#issuecomment-1553781868]
impl<'v, T> FromFormField<'v> for Maybe<T, rocket::form::Errors<'v>>
where
T: FromFormField<'v>,
{
fn from_value(field: ValueField<'v>) -> Result<Self, rocket::form::Errors<'v>> {
Ok(T::from_value(field).map_or_else(Maybe::Err, Maybe::Some))
}
fn default() -> Option<Self> {
Some(Maybe::Empty)
}
} Or is there anything I missed? |
Rocket Version: 0.5.0-rc.3
Description
FromData
implementation forOption<T>
causes malformed bodies to be silently ignored.To Reproduce
Sometimes a route handler needs to support an optional request body (for example, when making a backwards-compatible addition to an endpoint that previously required no request body).
So the definition of such a route looks like so:
The desired behavior is that, if no request data is present, we will get
None
for the body, and if some data is provided, we'll try to parse it. This is mostly how it works except, in the case where the body does not represent a validJson<Foo>
that is,Json::<Foo>::from_data
returns aFailure
result. Rather than a failure, we getNone
.Expected Behavior
If a request body is supplied, it should be validated.
Environment:
Additional Context
For prior art, we can look at what
serde
does. In general,serde
follows the model that optional values are validated if supplied, so the following fails:Playground
That is, because a value was provided for
field
, an incorrect value results in an error, not a silentNone
.It seems to me that rocket should follow this example because it provides stronger guarantees and more information.
If users want infallible parsing, they could use
Result
and just ignore any errors.Proposal for updating
FromData
implCurrently, the implementation of
FromData
forOption<T>
clearly swallows all errors:I propose this safer implementation of
FromData
:The text was updated successfully, but these errors were encountered: