Skip to content

Commit cc50b84

Browse files
committed
Parial Update for docs
1 parent ad9b26c commit cc50b84

File tree

3 files changed

+113
-17
lines changed

3 files changed

+113
-17
lines changed

docs/guide/01-upgrading.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,61 @@ summary = "a migration guide from Rocket v0.5 to v0.6"
66

77
This a placeholder for an eventual migration guide from v0.5 to v0.6.
88

9+
## Typed Errors and Catchers
10+
11+
Whenever a guard fails with a type, such as a `FromParam`, `FromRequest`, etc
12+
implementation, the error type is boxed up, and can be retrieved by catchers.
13+
This well-requested feature makes providing useful error types much easier.
14+
15+
The following example mounts a single route, and a catcher that will catch any
16+
invalid integer parameters. The catcher then formats the error into a JSON
17+
object and returns it.
18+
19+
TODO: add tests, and make this run?
20+
```rust,no_run
21+
# #[macro_use] extern crate rocket;
22+
use std::num::ParseIntError;
23+
24+
use rocket::Responder;
25+
use rocket::serde::{Serialize, json::Json};
26+
use rocket::request::FromParamError;
27+
28+
#[derive(Responder)]
29+
#[response(status = 422)]
30+
struct ParameterError<T>(T);
31+
32+
#[derive(Serialize)]
33+
#[serde(crate = "rocket::serde")]
34+
struct ErrorInfo<'a> {
35+
invalid_value: &'a str,
36+
description: String,
37+
}
38+
39+
#[catch(422, error = "<int_error>")]
40+
fn catch_invalid_int<'a>(
41+
// `&ParseIntError` would also work here, but `&FromParamError<ParseIntError>`
42+
// also gives us access to `raw`, the specific segment that failed to parse.
43+
int_error: &FromParamError<'a, ParseIntError>
44+
) -> ParameterError<Json<ErrorInfo<'a>>> {
45+
ParameterError(Json(ErrorInfo {
46+
invalid_value: int_error.raw,
47+
description: format!("{}", int_error.error),
48+
}))
49+
}
50+
51+
/// A simple route with a single parameter. If you pass this a valid `u8`,
52+
/// it will return a string. However, if you pass it something invalid as a `u8`,
53+
/// such as `-1`, `1234`, or `apple`, the catcher above will be respond with
54+
/// details on the error.
55+
#[get("/<number>")]
56+
fn number(number: u8) -> String {
57+
format!("You selected {}", number)
58+
}
59+
```
60+
61+
To see a full demonstration of this feature, check out the error-handling
62+
(TODO: link) example.
63+
964
## Getting Help
1065

1166
If you run into any issues upgrading, we encourage you to ask questions via

docs/guide/05-requests.md

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -519,22 +519,57 @@ As an example, for the `User` guard above, instead of allowing the guard to
519519
forward in `admin_panel_user`, we might want to detect it and handle it
520520
dynamically:
521521

522+
TODO: typed: This would actually make much more sense via a typed catcher
523+
522524
```rust
523525
# #[macro_use] extern crate rocket;
524526
# fn main() {}
525527

528+
use rocket::response::Redirect;
529+
use rocket::request::{self, Request, FromRequest, Outcome};
530+
use rocket::either::Either;
531+
use rocket::http::Status;
532+
526533
# type Template = ();
527-
# type AdminUser = rocket::http::Method;
528-
# type User = rocket::http::Method;
534+
// # type AdminUser = rocket::http::Method;
535+
// # type User = rocket::http::Method;
529536
# #[get("/login")]
530537
# fn login() -> Template { /* .. */ }
538+
# fn is_logged_in_as_admin(r: &Request<'_>) -> bool { true }
539+
# fn is_logged_in(r: &Request<'_>) -> bool { true }
540+
541+
#[derive(Debug, TypedError)]
542+
enum LoginError {
543+
NotLoggedIn,
544+
NotAdmin,
545+
}
546+
547+
struct AdminUser {}
548+
#[async_trait]
549+
impl<'r> FromRequest<'r> for AdminUser {
550+
type Error = LoginError;
551+
async fn from_request(r: &'r Request<'_>) -> Outcome<Self, Self::Error> {
552+
if is_logged_in_as_admin(r) {
553+
Outcome::Success(Self {})
554+
} else if is_logged_in(r) {
555+
Outcome::Error((Status::Unauthorized, LoginError::NotAdmin))
556+
} else {
557+
Outcome::Error((Status::Unauthorized, LoginError::NotLoggedIn))
558+
}
559+
}
560+
}
531561

532-
use rocket::response::Redirect;
562+
#[get("/admin")]
563+
fn admin_panel(user: AdminUser) -> &'static str {
564+
"Welcome to the admin panel"
565+
}
533566

534-
#[get("/admin", rank = 2)]
535-
fn admin_panel_user(user: Option<User>) -> Result<&'static str, Redirect> {
536-
let user = user.ok_or_else(|| Redirect::to(uri!(login)))?;
537-
Ok("Sorry, you must be an administrator to access this page.")
567+
#[catch(401, error = "<e>")]
568+
fn catch_login_error(e: &LoginError) -> Either<&'static str, Redirect> {
569+
match e {
570+
LoginError::NotLoggedIn => Either::Right(Redirect::to(uri!(login))),
571+
LoginError::NotAdmin => Either::Left("Admin permissions are required to view this page."),
572+
}
538573
}
539574
```
540575

docs/guide/06-responses.md

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -318,15 +318,14 @@ async fn files(file: PathBuf) -> Option<NamedFile> {
318318

319319
### `Result`
320320

321-
`Result` is another _wrapping_ responder: a `Result<T, E>` can only be returned
322-
when `T` implements `Responder` and `E` implements `Responder`.
321+
`Result` is a special responder, used to throw typed errors, so that they can
322+
later be caught by a typed catcher. `Result<T, E>` can only be used as a
323+
responder when `T` implements `Responder` and `E` implements `TypedError`.
323324

324-
The wrapped `Responder` in `Ok` or `Err`, whichever it might be, is used to
325-
respond to the client. This means that the responder can be chosen dynamically
326-
at run-time, and two different kinds of responses can be used depending on the
327-
circumstances. Revisiting our file server, for instance, we might wish to
328-
provide more feedback to the user when a file isn't found. We might do this as
329-
follows:
325+
The wrapped `Responder` in `Ok` will be used to respond directly to the client,
326+
but an `Err` value can be caught by a typed catcher. Revisiting our file server,
327+
for instance, we might wish to format error values when the file isn't found.
328+
We might do this as follows:
330329

331330
```rust
332331
# #[macro_use] extern crate rocket;
@@ -336,10 +335,17 @@ follows:
336335
use rocket::fs::NamedFile;
337336
use rocket::response::status::NotFound;
338337

338+
// `NotFound` is a wrapper over either responders or typed errors, that
339+
// sets the status to 404.
339340
#[get("/<file..>")]
340-
async fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
341+
async fn files(file: PathBuf) -> Result<NamedFile, NotFound<std::io::Error>> {
341342
let path = Path::new("static/").join(file);
342-
NamedFile::open(&path).await.map_err(|e| NotFound(e.to_string()))
343+
NamedFile::open(&path).await.map_err(|e| NotFound(e))
344+
}
345+
346+
#[catch(404, error = "<e>")]
347+
fn catch_std_io(e: &std::io::Error) -> String {
348+
format!("{}", e)
343349
}
344350
```
345351

0 commit comments

Comments
 (0)