Skip to content
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

RequestGuard return Custom Response on Failure #1232

Closed
PeterUlb opened this issue Feb 18, 2020 · 6 comments
Closed

RequestGuard return Custom Response on Failure #1232

PeterUlb opened this issue Feb 18, 2020 · 6 comments
Labels
enhancement A minor feature request request Request for new functionality

Comments

@PeterUlb
Copy link

PeterUlb commented Feb 18, 2020

I try to achieve the following:
I've created a RequestGuard as per https://api.rocket.rs/v0.4/rocket/request/trait.FromRequest.html#example-1

I've the requirement in rocket 0.4.2+ to return a JSON when the Authorization in that RequestGuard fails, with a custom code and message.
For Example { "code": "9001", message: "No header present" } or { "code": "9007", message: "Multiple header present"}

The only thing I can return is a Failure with a Status and my custom Error.

One solution as per documentation is to use Result in every route and match on that. But since the Authorization Request Guard is basically used in every route, that's tons of repetition for every route.

Is there anything I missed? (I'm fine with adding that RequestGuard to every route, but I'd like it to automatically generate my JSONError on failure without me needing to wrap it every time in a Result and match on it).

I'm not 100% sure if #749 is the same or similar. I'd still like to use RequestGuard

@sazzer
Copy link

sazzer commented Feb 20, 2020

I was just coming to raise exactly this.

One option is to write a Catcher for your status code, but as best I can tell you've then got to work out the problem again to return the correct payload.

@PeterUlb
Copy link
Author

Indeed, my current approach is to return a custom status code (e.g. 10000, 100001 and 10002) from the RequestGuard. In the catcher (create manually since the macro has a limit at 509) I change the status back to 401 and a specific error is returned (e.g. 100001 maps to {"code": "311", "message": "Too many authorization headers"}).
This only works for static responses without variables (fine for me right now) but feels like a super abuse.

@jebrosen
Copy link
Collaborator

AFAICT this question is the same as #749 but for request guards.

There are a few different things at play here:

  • What can request guards "early return"?
    • Currently, this is an error value + status code.
    • Early-returning a Response is possible in principle, but it hurts other use cases. For example, it is not easy to accomplish functionality like "If it's a Responder, use it to respond early, but if it's not a Responder, then the request guard can see it". Up through 0.4 we had something like this for Result<T, E> responses from routes, but it still needs the unstable specialization feature.
  • Who can observe that error value?
    • Currently, the only direct way is the Result<T, T::Error> request guard that can be used in a route.
    • It is also currently possible to indirectly observe the error value in a catcher by stashing the error value in request-local state (e.g. How is the Error type of FromRequest used? #1145 (comment)).
    • Supporting functionality like #[catch(422)] fn catch_db_error(error: DbError) is an error-handling goal, but it would require that all request guard error types be 'static and/or that we get the non_static_type_id functionality in rust (Typed Error Catchers, Fairings #749 (comment))

Much of this applies to fairings and data guards as well.


but I'd like it to automatically generate my JSONError on failure without me needing to wrap it every time in a Result and match on it

You can currently do something like this to reduce the boilerplate, but I agree it is not ideal:

#[get("/whoami")]
fn whoami(db: DbConn, auth_token: Result<AuthToken, JSONError>) -> Result<JsonValue, JSONError> {
    let user = auth_token?.username;
    json!({ "name": db.get_user(user).full_name })
}

In particular, the AuthToken request guard returns the same error type as the route so the ? operator can be used -- this would also work if AuthToken had an AuthTokenError, and there was an impl From<AuthTokenError> for JSONError.

@PeterUlb
Copy link
Author

Hm, actix-web does it quite well with the FromRequest trait for extractors. There I can return my ApiError with any data I like and it will be used as a Response. If I need the error, I can also wrap it in an Option and get access to the error type (are there other use-cases then these in rocket?).
Another alternative in actix is middleware. If my header is present, I extract it and add it to the request extensions. If it isn't present, I stop the request chain and return an error.

Maybe middleware would be something that wouldn't hurt other use-cases.

@SergioBenitez
Copy link
Member

@PeterUlb That requires + 'static, which we're trying to avoid.

Let's track this in #749.

@coffeebe4code
Copy link

#749 is closed, as well as this one, as well as another one.

Does this mean that this is complete? Is it possible in a guard now, or would I need to use a fairing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A minor feature request request Request for new functionality
Projects
None yet
Development

No branches or pull requests

5 participants