mirror of https://github.com/rwf2/Rocket.git
Tidy custom forward status changes, update docs.
This commit is contained in:
parent
055ad107df
commit
9b0564ed27
|
@ -117,8 +117,8 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
|
||||||
<Arc<[u8]> as Responder<'r, 'static>>
|
<Arc<[u8]> as Responder<'r, 'static>>
|
||||||
<Arc<str> as Responder<'r, 'static>>
|
<Arc<str> as Responder<'r, 'static>>
|
||||||
and $N others
|
and $N others
|
||||||
note: required by a bound in `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data<'o>>>::from`
|
note: required by a bound in `route::handler::<impl Outcome<rocket::Response<'o>, Status, (rocket::Data<'o>, Status)>>::from`
|
||||||
--> $WORKSPACE/core/lib/src/route/handler.rs
|
--> $WORKSPACE/core/lib/src/route/handler.rs
|
||||||
|
|
|
|
||||||
| pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
|
| pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
|
||||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::<impl Outcome<Response<'o>, Status, Data<'o>>>::from`
|
| ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::<impl Outcome<Response<'o>, Status, (Data<'o>, Status)>>::from`
|
||||||
|
|
|
@ -117,8 +117,8 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
|
||||||
<Arc<[u8]> as Responder<'r, 'static>>
|
<Arc<[u8]> as Responder<'r, 'static>>
|
||||||
<Arc<str> as Responder<'r, 'static>>
|
<Arc<str> as Responder<'r, 'static>>
|
||||||
and $N others
|
and $N others
|
||||||
note: required by a bound in `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data<'o>>>::from`
|
note: required by a bound in `route::handler::<impl Outcome<rocket::Response<'o>, Status, (rocket::Data<'o>, Status)>>::from`
|
||||||
--> $WORKSPACE/core/lib/src/route/handler.rs
|
--> $WORKSPACE/core/lib/src/route/handler.rs
|
||||||
|
|
|
|
||||||
| pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
|
| pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
|
||||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::<impl Outcome<Response<'o>, Status, Data<'o>>>::from`
|
| ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::<impl Outcome<Response<'o>, Status, (Data<'o>, Status)>>::from`
|
||||||
|
|
|
@ -64,7 +64,8 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
/// configured `ca_certs` and with respect to SNI, if any. See [module level
|
/// configured `ca_certs` and with respect to SNI, if any. See [module level
|
||||||
/// docs](crate::mtls) for configuration details.
|
/// docs](crate::mtls) for configuration details.
|
||||||
///
|
///
|
||||||
/// If the client does not present certificates, the guard _forwards_.
|
/// If the client does not present certificates, the guard _forwards_ with a
|
||||||
|
/// status of 401 Unauthorized.
|
||||||
///
|
///
|
||||||
/// If the certificate chain fails to validate or verify, the guard _fails_ with
|
/// If the certificate chain fails to validate or verify, the guard _fails_ with
|
||||||
/// the respective [`Error`].
|
/// the respective [`Error`].
|
||||||
|
@ -81,6 +82,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
/// use rocket::mtls::{self, bigint::BigUint, Certificate};
|
/// use rocket::mtls::{self, bigint::BigUint, Certificate};
|
||||||
/// use rocket::request::{Request, FromRequest, Outcome};
|
/// use rocket::request::{Request, FromRequest, Outcome};
|
||||||
/// use rocket::outcome::try_outcome;
|
/// use rocket::outcome::try_outcome;
|
||||||
|
/// use rocket::http::Status;
|
||||||
///
|
///
|
||||||
/// // The serial number for the certificate issued to the admin.
|
/// // The serial number for the certificate issued to the admin.
|
||||||
/// const ADMIN_SERIAL: &str = "65828378108300243895479600452308786010218223563";
|
/// const ADMIN_SERIAL: &str = "65828378108300243895479600452308786010218223563";
|
||||||
|
@ -97,7 +99,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
/// if let Some(true) = cert.has_serial(ADMIN_SERIAL) {
|
/// if let Some(true) = cert.has_serial(ADMIN_SERIAL) {
|
||||||
/// Outcome::Success(CertifiedAdmin(cert))
|
/// Outcome::Success(CertifiedAdmin(cert))
|
||||||
/// } else {
|
/// } else {
|
||||||
/// Outcome::Forward(())
|
/// Outcome::Forward(Status::Unauthorized)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
|
|
@ -22,10 +22,10 @@ impl<'r, S, E> IntoOutcome<S, (Status, E), (Data<'r>, Status)> for Result<S, E>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn or_forward(self, (data, error): (Data<'r>, Status)) -> Outcome<'r, S, E> {
|
fn or_forward(self, (data, status): (Data<'r>, Status)) -> Outcome<'r, S, E> {
|
||||||
match self {
|
match self {
|
||||||
Ok(val) => Success(val),
|
Ok(val) => Success(val),
|
||||||
Err(_) => Forward((data, error))
|
Err(_) => Forward((data, status))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl<S, E> IntoOutcome<S, (Status, E), Status> for Result<S, E> {
|
||||||
/// Extracts the [`Route`] from the request if one is available. When used
|
/// Extracts the [`Route`] from the request if one is available. When used
|
||||||
/// as a request guard in a route handler, this will always succeed. Outside
|
/// as a request guard in a route handler, this will always succeed. Outside
|
||||||
/// of a route handler, a route may not be available, and the request is
|
/// of a route handler, a route may not be available, and the request is
|
||||||
/// forwarded with a 500 status.
|
/// forwarded with a 500 Internal Server Error status.
|
||||||
///
|
///
|
||||||
/// For more information on when an `&Route` is available, see
|
/// For more information on when an `&Route` is available, see
|
||||||
/// [`Request::route()`].
|
/// [`Request::route()`].
|
||||||
|
@ -165,19 +165,19 @@ impl<S, E> IntoOutcome<S, (Status, E), Status> for Result<S, E> {
|
||||||
///
|
///
|
||||||
/// Extracts the [`ContentType`] from the incoming request via
|
/// Extracts the [`ContentType`] from the incoming request via
|
||||||
/// [`Request::content_type()`]. If the request didn't specify a
|
/// [`Request::content_type()`]. If the request didn't specify a
|
||||||
/// Content-Type, the request is forwarded.
|
/// Content-Type, the request is forwarded with a 404 Not Found status.
|
||||||
///
|
///
|
||||||
/// * **IpAddr**
|
/// * **IpAddr**
|
||||||
///
|
///
|
||||||
/// Extracts the client ip address of the incoming request as an [`IpAddr`]
|
/// Extracts the client ip address of the incoming request as an [`IpAddr`]
|
||||||
/// via [`Request::client_ip()`]. If the client's IP address is not known,
|
/// via [`Request::client_ip()`]. If the client's IP address is not known,
|
||||||
/// the request is forwarded.
|
/// the request is forwarded with a 404 Not Found status.
|
||||||
///
|
///
|
||||||
/// * **SocketAddr**
|
/// * **SocketAddr**
|
||||||
///
|
///
|
||||||
/// Extracts the remote address of the incoming request as a [`SocketAddr`]
|
/// Extracts the remote address of the incoming request as a [`SocketAddr`]
|
||||||
/// via [`Request::remote()`]. If the remote address is not known, the
|
/// via [`Request::remote()`]. If the remote address is not known, the
|
||||||
/// request is forwarded.
|
/// request is forwarded with a 404 Not Found status.
|
||||||
///
|
///
|
||||||
/// * **Option<T>** _where_ **T: FromRequest**
|
/// * **Option<T>** _where_ **T: FromRequest**
|
||||||
///
|
///
|
||||||
|
@ -193,7 +193,7 @@ impl<S, E> IntoOutcome<S, (Status, E), Status> for Result<S, E> {
|
||||||
/// `FromRequest` implementation. If derivation is a `Success`, the value is
|
/// `FromRequest` implementation. If derivation is a `Success`, the value is
|
||||||
/// returned in `Ok`. If the derivation is a `Failure`, the error value is
|
/// returned in `Ok`. If the derivation is a `Failure`, the error value is
|
||||||
/// returned in `Err`. If the derivation is a `Forward`, the request is
|
/// returned in `Err`. If the derivation is a `Forward`, the request is
|
||||||
/// forwarded.
|
/// forwarded with the same status code as the original forward.
|
||||||
///
|
///
|
||||||
/// [`Config`]: crate::config::Config
|
/// [`Config`]: crate::config::Config
|
||||||
///
|
///
|
||||||
|
@ -503,7 +503,7 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
|
||||||
match T::from_request(request).await {
|
match T::from_request(request).await {
|
||||||
Success(val) => Success(Ok(val)),
|
Success(val) => Success(Ok(val)),
|
||||||
Failure((_, e)) => Success(Err(e)),
|
Failure((_, e)) => Success(Err(e)),
|
||||||
Forward(_) => Forward(Status::NotFound),
|
Forward(status) => Forward(status),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -519,3 +519,12 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[crate::async_trait]
|
||||||
|
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Outcome<T, T::Error> {
|
||||||
|
type Error = std::convert::Infallible;
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
|
Success(T::from_request(request).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -221,8 +221,8 @@ impl<'r, 'o: 'r> Outcome<'o> {
|
||||||
/// Return the `Outcome` of response to `req` from `responder`.
|
/// Return the `Outcome` of response to `req` from `responder`.
|
||||||
///
|
///
|
||||||
/// If the responder returns `Ok`, an outcome of `Success` is returned with
|
/// If the responder returns `Ok`, an outcome of `Success` is returned with
|
||||||
/// the response. If the responder returns `Err`, an outcome of `Forward` is
|
/// the response. If the responder returns `Err`, an outcome of `Forward`
|
||||||
/// returned.
|
/// with a status of `404 Not Found` is returned.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
|
|
@ -80,7 +80,7 @@ use crate::http::Status;
|
||||||
/// // Or alternatively, using `Rocket::state()`:
|
/// // Or alternatively, using `Rocket::state()`:
|
||||||
/// let outcome = request.rocket().state::<MyConfig>()
|
/// let outcome = request.rocket().state::<MyConfig>()
|
||||||
/// .map(|my_config| Item(&my_config.user_val))
|
/// .map(|my_config| Item(&my_config.user_val))
|
||||||
/// .or_forward(Status::NotFound);
|
/// .or_forward(Status::InternalServerError);
|
||||||
///
|
///
|
||||||
/// outcome
|
/// outcome
|
||||||
/// }
|
/// }
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
#[macro_use] extern crate rocket;
|
|
||||||
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::request::{self, Request, FromRequest};
|
|
||||||
|
|
||||||
pub struct Authenticated;
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl<'r> FromRequest<'r> for Authenticated {
|
|
||||||
type Error = std::convert::Infallible;
|
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
|
||||||
if request.headers().contains("Authenticated") {
|
|
||||||
request::Outcome::Success(Authenticated)
|
|
||||||
} else {
|
|
||||||
request::Outcome::Forward(Status::Unauthorized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/one")]
|
|
||||||
pub async fn get_protected_one(_user: Authenticated) -> &'static str {
|
|
||||||
"Protected"
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/one", rank = 2)]
|
|
||||||
pub async fn get_public_one() -> &'static str {
|
|
||||||
"Public"
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/two")]
|
|
||||||
pub async fn get_protected_two(_user: Authenticated) -> &'static str {
|
|
||||||
"Protected"
|
|
||||||
}
|
|
||||||
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use rocket::routes;
|
|
||||||
use rocket::local::blocking::Client;
|
|
||||||
use rocket::http::{Header, Status};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn one_protected_returned_for_authenticated() {
|
|
||||||
let rocket = rocket::build().mount("/",
|
|
||||||
routes![get_protected_one, get_public_one, get_protected_two]);
|
|
||||||
|
|
||||||
let client = Client::debug(rocket).unwrap();
|
|
||||||
let req = client.get("/one").header(Header::new("Authenticated", "true"));
|
|
||||||
let response = req.dispatch();
|
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.into_string(), Some("Protected".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn one_public_returned_for_unauthenticated() {
|
|
||||||
let rocket = rocket::build().mount("/",
|
|
||||||
routes![get_protected_one, get_public_one, get_protected_two]);
|
|
||||||
|
|
||||||
let client = Client::debug(rocket).unwrap();
|
|
||||||
let req = client.get("/one");
|
|
||||||
let response = req.dispatch();
|
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
assert_eq!(response.into_string(), Some("Public".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_unauthorized_returned_for_unauthenticated() {
|
|
||||||
let rocket = rocket::build().mount("/",
|
|
||||||
routes![get_protected_one, get_public_one, get_protected_two]);
|
|
||||||
|
|
||||||
let client = Client::debug(rocket).unwrap();
|
|
||||||
let req = client.get("/two");
|
|
||||||
let response = req.dispatch();
|
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::Unauthorized);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
#[macro_use] extern crate rocket;
|
||||||
|
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::request::{self, Request, FromRequest};
|
||||||
|
|
||||||
|
struct Authenticated;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for Authenticated {
|
||||||
|
type Error = std::convert::Infallible;
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
if request.headers().contains("Authenticated") {
|
||||||
|
request::Outcome::Success(Authenticated)
|
||||||
|
} else {
|
||||||
|
request::Outcome::Forward(Status::Unauthorized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TeapotForward;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for TeapotForward {
|
||||||
|
type Error = std::convert::Infallible;
|
||||||
|
|
||||||
|
async fn from_request(_: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
request::Outcome::Forward(Status::ImATeapot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/auth")]
|
||||||
|
fn auth(_name: Authenticated) -> &'static str {
|
||||||
|
"Protected"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/auth", rank = 2)]
|
||||||
|
fn public() -> &'static str {
|
||||||
|
"Public"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/auth", rank = 3)]
|
||||||
|
fn teapot(_teapot: TeapotForward) -> &'static str {
|
||||||
|
"Protected"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/need-auth")]
|
||||||
|
fn auth_needed(_auth: Authenticated) -> &'static str {
|
||||||
|
"Have Auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[catch(401)]
|
||||||
|
fn catcher() -> &'static str {
|
||||||
|
"Custom Catcher"
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rocket::routes;
|
||||||
|
use rocket::local::blocking::Client;
|
||||||
|
use rocket::http::{Header, Status};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authorized_forwards() {
|
||||||
|
let client = Client::debug_with(routes![auth, public, auth_needed]).unwrap();
|
||||||
|
|
||||||
|
let response = client.get("/auth")
|
||||||
|
.header(Header::new("Authenticated", "true"))
|
||||||
|
.dispatch();
|
||||||
|
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
assert_eq!(response.into_string().unwrap(), "Protected");
|
||||||
|
|
||||||
|
let response = client.get("/auth").dispatch();
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
assert_eq!(response.into_string().unwrap(), "Public");
|
||||||
|
|
||||||
|
let response = client.get("/need-auth")
|
||||||
|
.header(Header::new("Authenticated", "true"))
|
||||||
|
.dispatch();
|
||||||
|
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
assert_eq!(response.into_string().unwrap(), "Have Auth");
|
||||||
|
|
||||||
|
let response = client.get("/need-auth").dispatch();
|
||||||
|
assert_eq!(response.status(), Status::Unauthorized);
|
||||||
|
assert!(response.into_string().unwrap().contains("Rocket"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unauthorized_custom_catcher() {
|
||||||
|
let rocket = rocket::build()
|
||||||
|
.mount("/", routes![auth_needed])
|
||||||
|
.register("/", catchers![catcher]);
|
||||||
|
|
||||||
|
let client = Client::debug(rocket).unwrap();
|
||||||
|
let response = client.get("/need-auth").dispatch();
|
||||||
|
assert_eq!(response.status(), Status::Unauthorized);
|
||||||
|
assert_eq!(response.into_string().unwrap(), "Custom Catcher");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn use_last_forward() {
|
||||||
|
let client = Client::debug_with(routes![auth, teapot]).unwrap();
|
||||||
|
let response = client.get("/auth").dispatch();
|
||||||
|
assert_eq!(response.status(), Status::ImATeapot);
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ impl<'r> FromRequest<'r> for User {
|
||||||
.get_private("user_id")
|
.get_private("user_id")
|
||||||
.and_then(|cookie| cookie.value().parse().ok())
|
.and_then(|cookie| cookie.value().parse().ok())
|
||||||
.map(User)
|
.map(User)
|
||||||
.or_forward(Status::NotFound)
|
.or_forward(Status::Unauthorized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,9 +206,10 @@ fn hello(name: &str, age: u8, cool: bool) { /* ... */ }
|
||||||
|
|
||||||
What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? When a parameter
|
What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? When a parameter
|
||||||
type mismatch occurs, Rocket _forwards_ the request to the next matching route,
|
type mismatch occurs, Rocket _forwards_ the request to the next matching route,
|
||||||
if there is any. This continues until a route doesn't forward the request or
|
if there is any. This continues until a route succeeds or fails, or there are no
|
||||||
there are no remaining routes to try. When there are no remaining routes, a
|
other matching routes to try. When there are no remaining routes, the [error
|
||||||
customizable **404 error** is returned.
|
catcher](#error-catchers) associated with the status set by the last forwarding
|
||||||
|
guard is called.
|
||||||
|
|
||||||
Routes are attempted in increasing _rank_ order. Rocket chooses a default
|
Routes are attempted in increasing _rank_ order. Rocket chooses a default
|
||||||
ranking from -12 to -1, detailed in the next section, but a route's rank can also
|
ranking from -12 to -1, detailed in the next section, but a route's rank can also
|
||||||
|
@ -436,13 +437,14 @@ We start with two request guards:
|
||||||
|
|
||||||
The `FromRequest` implementation for `User` checks that a cookie identifies
|
The `FromRequest` implementation for `User` checks that a cookie identifies
|
||||||
a user and returns a `User` value if so. If no user can be authenticated,
|
a user and returns a `User` value if so. If no user can be authenticated,
|
||||||
the guard forwards.
|
the guard forwards with a 401 Unauthorized status.
|
||||||
|
|
||||||
* `AdminUser`: A user authenticated as an administrator.
|
* `AdminUser`: A user authenticated as an administrator.
|
||||||
|
|
||||||
The `FromRequest` implementation for `AdminUser` checks that a cookie
|
The `FromRequest` implementation for `AdminUser` checks that a cookie
|
||||||
identifies an _administrative_ user and returns an `AdminUser` value if so.
|
identifies an _administrative_ user and returns an `AdminUser` value if so.
|
||||||
If no user can be authenticated, the guard forwards.
|
If no user can be authenticated, the guard forwards with a 401 Unauthorized
|
||||||
|
status.
|
||||||
|
|
||||||
We now use these two guards in combination with forwarding to implement the
|
We now use these two guards in combination with forwarding to implement the
|
||||||
following three routes, each leading to an administrative control panel at
|
following three routes, each leading to an administrative control panel at
|
||||||
|
|
|
@ -141,14 +141,13 @@ impl<'r> FromRequest<'r> for Item<'r> {
|
||||||
// Or alternatively, using `Rocket::state()`:
|
// Or alternatively, using `Rocket::state()`:
|
||||||
let outcome = request.rocket().state::<MyConfig>()
|
let outcome = request.rocket().state::<MyConfig>()
|
||||||
.map(|my_config| Item(&my_config.user_val))
|
.map(|my_config| Item(&my_config.user_val))
|
||||||
.or_forward(Status::NotFound);
|
.or_forward(Status::InternalServerError);
|
||||||
|
|
||||||
outcome
|
outcome
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
[`Request::guard()`]: @api/rocket/struct.Request.html#method.guard
|
[`Request::guard()`]: @api/rocket/struct.Request.html#method.guard
|
||||||
[`Rocket::state()`]: @api/rocket/struct.Rocket.html#method.state
|
[`Rocket::state()`]: @api/rocket/struct.Rocket.html#method.state
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue