diff --git a/site/guide.md b/site/guide.md index d289a41e..ae51f319 100644 --- a/site/guide.md +++ b/site/guide.md @@ -3,8 +3,8 @@ Welcome to Rocket! This is the official guide. It is designed to serve as a starting point to -writing web application with Rocket and Rust. The guide is also designed to be a -reference for experienced Rocket developers. This guide is conversational in +writing web applications with Rocket and Rust. The guide is also designed to be +a reference for experienced Rocket developers. This guide is conversational in tone. For concise and purely technical documentation, see the [API documentation](https://api.rocket.rs). @@ -21,7 +21,8 @@ aspect of Rocket. The sections are: parsing, and validating. - **[Responses](responses/):** discusses generating responses. - **[State](state/):** how to manage state in a Rocket application. - - **[Fairings](fairings/):** introduction to Rocket's structure middleware. + - **[Fairings](fairings/):** provides an overview of Rocket's structured + middleware. - **[Testing](testing/):** how to unit and integration test a Rocket application. - **[Configuration](configuration/):** how to configure a Rocket application. diff --git a/site/guide/configuration.md b/site/guide/configuration.md index af7aa98f..1ef104de 100644 --- a/site/guide/configuration.md +++ b/site/guide/configuration.md @@ -63,7 +63,7 @@ value: [development] address = "localhost" port = 8000 -workers = [number_of_cpus * 2] +workers = [number of cpus * 2] log = "normal" secret_key = [randomly generated at launch] limits = { forms = 32768 } @@ -71,7 +71,7 @@ limits = { forms = 32768 } [staging] address = "0.0.0.0" port = 80 -workers = [number_of_cpus * 2] +workers = [number of cpus * 2] log = "normal" secret_key = [randomly generated at launch] limits = { forms = 32768 } @@ -79,7 +79,7 @@ limits = { forms = 32768 } [production] address = "0.0.0.0" port = 80 -workers = [number_of_cpus * 2] +workers = [number of cpus * 2] log = "critical" secret_key = [randomly generated at launch] limits = { forms = 32768 } @@ -98,7 +98,7 @@ overrides if already present, that parameter in every environment. For example, given the following `Rocket.toml` file, the value of `address` will be `"1.2.3.4"` in every environment: -```toml +``` [global] address = "1.2.3.4" @@ -119,7 +119,7 @@ application, can use them as they wish. As an example, the accepts a value for the `template_dir` configuration parameter. The parameter can be set in `Rocket.toml` as follows: -```toml +``` [development] template_dir = "dev_templates/" @@ -179,7 +179,7 @@ parameter. The value of `tls` must be a table with two keys: The recommended way to specify these parameters is via the `global` environment: -```toml +``` [global.tls] certs = "/path/to/certs.pem" key = "/path/to/key.pem" @@ -187,7 +187,7 @@ key = "/path/to/key.pem" Of course, you can always specify the configuration values per environment: -```toml +``` [development] tls = { certs = "/path/to/certs.pem", key = "/path/to/key.pem" } ``` @@ -196,7 +196,7 @@ In order for TLS support to be enabled, Rocket must be compiled with the `"tls"` feature. To do this, add the `"tls"` feature to the `rocket` dependency in your `Cargo.toml` file: -```toml +``` [dependencies] rocket = { version = "0.2.8", features = ["tls"] } ``` diff --git a/site/guide/getting-started.md b/site/guide/getting-started.md index 8f499a74..d6d67cbf 100644 --- a/site/guide/getting-started.md +++ b/site/guide/getting-started.md @@ -28,7 +28,7 @@ Rocket project by running the following command in the directory: rustup override set nightly ``` -### Minimum Nightly Version +### Minimum Nightly Rocket always requires the _latest_ version of Rust nightly. If your Rocket application suddently stops building, ensure you're using the latest version of @@ -86,7 +86,7 @@ run`. You should see the following: => address: localhost => port: 8000 => log: normal - => workers: [logical core count * 2] + => workers: [core count * 2] => secret key: generated => limits: forms = 32KiB => tls: disabled diff --git a/site/guide/overview.md b/site/guide/overview.md index 06802331..a653ba44 100644 --- a/site/guide/overview.md +++ b/site/guide/overview.md @@ -40,7 +40,8 @@ lifecycle as the following sequence of steps: The remainder of this section details the _routing_ phase as well as additional components needed for Rocket to begin dispatching requests to request handlers. -The sections following describe the request and response phases. +The sections following describe the request and response phases as well as other +components of Rocket. ## Routing @@ -77,12 +78,14 @@ constructing routes. Before Rocket can dispatch requests to a route, the route needs to be _mounted_. Mounting a route is like namespacing it. Routes are mounted via the `mount` -method on a `Rocket` instance which are themselves created with the +method on a `Rocket` instance. A `Rocket` instance is typically created with the `rocket::ignite()` static method. -The `mount` method takes **1)** a path to namespace a list of routes under, and -**2)** a list of route handlers through the `routes!` macro. The `routes!` macro -ties Rocket's code generation to your application. +The `mount` method takes: + + 1. A path to namespace a list of routes under, + 2. A list of route handlers through the `routes!` macro, tying Rocket's code + generation to your application. For instance, to mount the `world` route we declared above, we can write the following: diff --git a/site/guide/quickstart.md b/site/guide/quickstart.md index 125bd22b..a6d913a7 100644 --- a/site/guide/quickstart.md +++ b/site/guide/quickstart.md @@ -2,9 +2,9 @@ Before you can start writing a Rocket application, you'll need a **nightly** version of Rust installed. We recommend you use [rustup](https://rustup.rs/) to -install or configure such a version. If you don't have Rust installed and want -extra guidance installing it, the [getting started](/guide/getting-started) -section provides a guide. +install or configure such a version. If you don't have Rust installed and would +like extra guidance doing so, see the [getting started](/guide/getting-started) +section. ## Running Examples diff --git a/site/guide/requests.md b/site/guide/requests.md index 738c474f..bc353b79 100644 --- a/site/guide/requests.md +++ b/site/guide/requests.md @@ -23,8 +23,8 @@ Among other things, you can ask Rocket to automatically validate: The route attribute and function signature work in tandem to describe these validations. Rocket's code generation takes care of actually validating the -proprerties. The remainder of this section describes how to ask Rocket to -validate against all of these properties and more. +properties. This section describes how to ask Rocket to validate against all of +these properties and more. ## Methods @@ -38,7 +38,9 @@ to the root path: ``` The grammar for these attributes is defined formally in the -[rocket_codegen](https://api.rocket.rs/rocket_codegen/) API docs. +[`rocket_codegen`](https://api.rocket.rs/rocket_codegen/) API docs. + +### HEAD Requests Rocket handles `HEAD` requests automatically when there exists a `GET` route that would otherwise match. It does this by stripping the body from the @@ -46,10 +48,10 @@ response, if there is one. You can also specialize the handling of a `HEAD` request by declaring a route for it; Rocket won't interfere with `HEAD` requests your application handles. -### Reinterpreting Methods +### Reinterpreting -Because browsers only send `GET` and `POST` requests, Rocket _reinterprets_ -requests under certain conditions. If a `POST` request contains a body of +Because browsers can only send `GET` and `POST` requests, Rocket _reinterprets_ +request methods under certain conditions. If a `POST` request contains a body of `Content-Type: application/x-www-form-urlencoded`, and the form's **first** field has the name `_method` and a valid HTTP method name as its value (such as `"PUT"`), that field's value is used as the method for the incoming request. @@ -60,7 +62,7 @@ makes use of this feature to submit `PUT` and `DELETE` requests from a web form. ## Dynamic Segments You can declare path segments as dynamic by using angle brackets around variable -names in a route's path. For example, if we wanted to say _Hello!_ to anything, +names in a route's path. For example, if we want to say _Hello!_ to anything, not just the world, we can declare a route like so: ```rust @@ -76,11 +78,11 @@ any request to a path with two non-empty segments, where the first segment is visit `/hello/John`, the application would respond with `Hello, John!`. Any number of dynamic path segments are allowed. A path segment can be of any -type, including your own, as long as the type implements the [`FromParam`]. -Rocket implements `FromParam` for many of the standard library types, as well as -a few special Rocket types. For the full list of supplied implementations, see -the [`FromParam` API docs]. Here's a more complete route to illustrate varied -usage: +type, including your own, as long as the type implements the [`FromParam`] +trait. Rocket implements `FromParam` for many of the standard library types, as +well as a few special Rocket types. For the full list of supplied +implementations, see the [`FromParam` API docs]. Here's a more complete route to +illustrate varied usage: ```rust #[get("/hello///")] @@ -124,11 +126,11 @@ example: fn hello(name: String, age: u8, cool: bool) -> String { ... } ``` -What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? In this case, -Rocket _forwards_ the request to the next matching route, if there is any. This -continues until a route doesn't forward the request or there are no remaining -routes to try. When there are no remaining routes, a customizable **404 error** -is returned. +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, +if there is any. This continues until a route doesn't forward the request or +there are no remaining routes to try. When there are no remaining routes, a +customizable **404 error** is returned. Routes are attempted in increasing _rank_ order. Rocket chooses a default ranking from -4 to -1, detailed in the next section, for all routes, but a @@ -167,9 +169,9 @@ would never forward. An `Ok` variant would indicate that `` was a valid `Err`'s value would contain the string that failed to parse as a `usize`. By the way, if you were to omit the `rank` parameter in the `user_str` or -`user_int` routes, Rocket would emit a warning indicating that the routes -_collide_, or can match against similar incoming requests. The `rank` parameter -resolves this collision. +`user_int` routes, Rocket would emit an error and abort launch, indicating that +the routes _collide_, or can match against similar incoming requests. The `rank` +parameter resolves this collision. ### Default Ranking @@ -181,14 +183,14 @@ of a route given its properties. | static path | query string | rank | example | | ------------- | -------------- | ------ | ------------------- | -| yes | yes | -4 | /hello?world=true | -| yes | no | -3 | /hello | -| no | yes | -2 | /<hi>?world=true | -| no | no | -1 | /<hi> | +| yes | yes | -4 | `/hello?world=true` | +| yes | no | -3 | `/hello` | +| no | yes | -2 | `/?world=true` | +| no | no | -1 | `/` | -## Many Dynamic Segments +## Multiple Segments -You can also match against multiple segments by using `` in the route +You can also match against multiple segments by using `` in a route path. The type of such parameters, known as _segments_ parameters, must implement [`FromSegments`]. Segments parameters must be the final component of a path: any text after a segments parameter will result in a compile-time error. @@ -235,8 +237,8 @@ fn new_user(user: JSON) -> T { ... } ``` The `format` parameter in the `post` attribute declares that only incoming -requests with `Content-Type: application/json` will match. (The `data` parameter -is described in the next section.) +requests with `Content-Type: application/json` will match `new_user`. (The +`data` parameter is described in the next section.) When a route indicates a non-payload-supporting method (`GET`, `HEAD`, and `OPTIONS`), the `format` route parameter instructs Rocket to check against the @@ -253,7 +255,7 @@ fn user(id: usize) -> JSON { ... } The `format` parameter in the `get` attribute declares that only incoming requests with `application/json` as the preferred media type in the `Accept` -header will match. +header will match `user`. ## Request Guards @@ -274,7 +276,7 @@ As an example, the following dummy handler makes use of three request guards, `A`, `B`, and `C`. An input can be identified as a request guard if it is not named in the route attribute. This is why `param` is not a request guard. -```rust,ignore +```rust #[get("/")] fn index(param: isize, a: A, b: B, c: C) -> ... { ... } ``` @@ -288,31 +290,6 @@ documentation. [`FromRequest`]: https://api.rocket.rs/rocket/request/trait.FromRequest.html [`Cookies`]: https://api.rocket.rs/rocket/http/enum.Cookies.html -### Retrieving Metadata - -Sometimes we need data associated with a request that isn't a direct data input. -Rocket makes retrieving and validating such information easy through request -guards. As an example, consider the built-in [`Cookies`] request guard. Because -`Cookies` is a request guard, an argument of its type can simply be added to a -handler: - -```rust -use rocket::http::Cookies; - -#[get("/")] -fn index(cookies: Cookies) -> Option { - cookies.get("message") - .map(|value| format!("Message: {}", value)) -} -``` - -This results in the incoming request's cookies being accessible from the -handler. The [cookies example] on GitHub illustrates further use of the -`Cookies` type to get and set cookies, while the [`Cookies`] documentation -contains full usage information. - -[cookies example]: https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/cookies - ### Custom Guards You can implement `FromRequest` for your own types. For instance, to protect a @@ -325,12 +302,11 @@ then use it as a request guard: fn sensitive(key: ApiKey) -> &'static str { ... } ``` -You might also implement `FromRequest` for an `AdminUser` type that validates -that the cookies in the incoming request authenticate an administrator. Then, -any handler with an `AdminUser` or `ApiKey` type in its argument list is assured -to only be invoked if the appropriate conditions are met. Request guards -centralize policies, resulting in a simpler, safer, and more secure -applications. +You might also implement `FromRequest` for an `AdminUser` type that +authenticates an administrator using incoming cookies. Then, any handler with an +`AdminUser` or `ApiKey` type in its argument list is assured to only be invoked +if the appropriate conditions are met. Request guards centralize policies, +resulting in a simpler, safer, and more secure applications. ### Forwarding Guards @@ -353,7 +329,8 @@ We start with two request guards: If no user can be authenticated, the guard forwards. We now use these two guards in combination with forwarding to implement the -following three routes to some administrative panel at `/admin`: +following three routes, each leading to an administrative control panel at +`/admin`: ```rust #[get("/admin")] @@ -362,7 +339,7 @@ fn admin_panel(admin: AdminUser) -> &'static str { } #[get("/admin", rank = 2)] -fn admin_panel_user(admin: User) -> &'static str { +fn admin_panel_user(user: User) -> &'static str { "Sorry, you must be an administrator to access this page." } @@ -376,18 +353,134 @@ The three routes above encode authentication _and_ authorization. The `admin_panel` route only succeeds if an administrator is logged in. Only then is the admin panel displayed. If the user is not an admin, the `AdminUser` route will forward. Since the `admin_panel_user` route is ranked next highest, it is -tried next. This route succeeds if there is _any_ user signed in, and an +attempted next. This route succeeds if there is _any_ user signed in, and an authorization failure message is displayed. Finally, if a user isn't signed in, -the `admin_panel_redirect` route is tried. Since this route has no guards, it -always succeeds. The user is redirected to a log in page. +the `admin_panel_redirect` route is attempted. Since this route has no guards, +it always succeeds. The user is redirected to a log in page. + +## Cookies + +[`Cookies`] is an important, built-in request guard: it allows you to get, set, +and remove cookies. Because `Cookies` is a request guard, an argument of its +type can simply be added to a handler: + +```rust +use rocket::http::Cookies; + +#[get("/")] +fn index(cookies: Cookies) -> Option { + cookies.get("message") + .map(|value| format!("Message: {}", value)) +} +``` + +This results in the incoming request's cookies being accessible from the +handler. The example above retrieves a cookie named `message`. Cookies can also +be set and removed using the `Cookies` guard. The [cookies example] on GitHub +illustrates further use of the `Cookies` type to get and set cookies, while the +[`Cookies`] documentation contains complete usage information. + +[cookies example]: https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/cookies + +### Private Cookies + +Cookies added via the [`Cookies::add()`] method are set _in the clear._ In other +words, the value set is visible by the client. For sensitive data, Rocket +provides _private_ cookies. + +Private cookies are just like regular cookies except that they are encrypted +using authenticated encryption, a form of encryption which simultaneously +provides confidentiality, integrity, and authenticity. This means that private +cookies cannot be inspected, tampered with, or manufactured by clients. If you +prefer, you can think of private cookies as being signed and encrypted. + +The API for retrieving, adding, and removing private cookies is identical except +methods are suffixed with `_private`. These methods are: [`get_private`], +[`add_private`], and [`remove_private`]. An example of their usage is below: + +```rust +/// Retrieve the user's ID, if any. +#[get("/user_id")] +fn user_id(cookies: Cookies) -> Option { + request.cookies() + .get_private("user_id") + .map(|cookie| format!("User ID: {}", cookie.value())) +} + +/// Remove the `user_id` cookie. +#[post("/logout")] +fn logout(mut cookies: Cookies) -> Flash { + cookies.remove_private(Cookie::named("user_id")); + Flash::success(Redirect::to("/login"), "Successfully logged out.") +} +``` + +[`Cookies::add()`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.add + +### Secret Key + +To encrypt private cookies, Rocket uses the 256-bit key specified in the +`secret_key` configuration parameter. If one is not specified, Rocket will +automatically generate a fresh key. Note, however, that a private cookie can +only be decrypted with the same key with which it was encrypted. As such, it is +important to set a `secret_key` configuration parameter when using private +cookies so that cookies decrypt properly after an application restart. Rocket +emits a warning if an application is run in production without a configured +`secret_key`. + +Generating a string suitable for use as a `secret_key` configuration value is +usually done through tools like `openssl`. Using `openssl`, a 256-bit base64 key +can be generated with the command `openssl rand -base64 32`. + +For more information on configuration, see the +[Configuration](/guide/configuration) section of the guide. + +[`get_private`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.get_private +[`add_private`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.add_private +[`remove_private`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.remove_private + +### One-At-A-Time + +For safety reasons, Rocket currently requires that at most one `Cookies` +instance be active at a time. It's uncommon to run into this restriction, but it +can be confusing to handle if it does crop up. + +If this does happen, Rocket will emit messages to the console that look as +follows: + +``` +=> Error: Multiple `Cookies` instances are active at once. +=> An instance of `Cookies` must be dropped before another can be retrieved. +=> Warning: The retrieved `Cookies` instance will be empty. +``` + +The messages will be emitted when a violating handler is called. The issue can +be resolved by ensuring that two instances of `Cookies` cannot be active at once +due to the offending handler. A common error is to have a handler that uses a +`Cookies` request guard as well as a `Custom` request guard that retrieves +`Cookies`, as so: + +```rust +#[get("/")] +fn bad(cookies: Cookies, custom: Custom) { .. } +``` + +Because the `cookies` guard will fire before the `custom` guard, the `custom` +guard will retrieve an instance of `Cookies` when one already exists for +`cookies`. This scenario can be fixed by simply swapping the order of the +guards: + +```rust +#[get("/")] +fn good(custom: Custom, cookies: Cookies) { .. } +``` ## Body Data At some point, your web application will need to process body data. Data processing, like much of Rocket, is type directed. To indicate that a handler expects data, annotate it with `data = ""`, where `param` is an argument -in the handler. The argument's type must implement the -[FromData](https://api.rocket.rs/rocket/data/trait.FromData.html) trait. It +in the handler. The argument's type must implement the [`FromData`] trait. It looks like this, where `T: FromData`: ```rust @@ -395,6 +488,8 @@ looks like this, where `T: FromData`: fn new(input: T) -> String { ... } ``` +[`FromData`]: https://api.rocket.rs/rocket/data/trait.FromData.html + ### Forms Forms are the most common type of data handled in web applications, and Rocket @@ -415,23 +510,23 @@ fn new(task: Form) -> String { ... } ``` The `Form` type implements the `FromData` trait as long as its generic parameter -implements the -[FromForm](https://api.rocket.rs/rocket/request/trait.FromForm.html) trait. In -the example, we've derived the `FromForm` trait automatically for the `Task` -structure. `FromForm` can be derived for any structure whose fields implement -[FromFormValue](https://api.rocket.rs/rocket/request/trait.FromFormValue.html). -If a `POST /todo` request arrives, the form data will automatically be parsed -into the `Task` structure. If the data that arrives isn't of the correct -Content-Type, the request is forwarded. If the data doesn't parse or is simply -invalid, a customizable `400 - Bad Request` or `422 - Unprocessable Entity` -error is returned. As before, a forward or failure can be caught by using the -`Option` and `Result` types: +implements the [`FromForm`] trait. In the example, we've derived the `FromForm` +trait automatically for the `Task` structure. `FromForm` can be derived for any +structure whose fields implement [`FromFormValue`]. If a `POST /todo` request +arrives, the form data will automatically be parsed into the `Task` structure. +If the data that arrives isn't of the correct Content-Type, the request is +forwarded. If the data doesn't parse or is simply invalid, a customizable `400 - +Bad Request` or `422 - Unprocessable Entity` error is returned. As before, a +forward or failure can be caught by using the `Option` and `Result` types: ```rust #[post("/todo", data = "")] fn new(task: Option>) -> String { ... } ``` +[`FromForm`]: https://api.rocket.rs/rocket/request/trait.FromForm.html +[`FromFormValue`]: https://api.rocket.rs/rocket/request/trait.FromFormValue.html + #### Lenient Parsing Rocket's `FromForm` parsing is _strict_ by default. In other words, A `Form` @@ -489,7 +584,7 @@ Rocket will then match the form field named `type` to the structure field named #### Field Validation Fields of forms can be easily validated via implementations of the -`FromFormValue` trait. For example, if you'd like to verify that some user is +[`FromFormValue`] trait. For example, if you'd like to verify that some user is over some age in a form, then you might define a new `AdultAge` type, use it as a field in a form structure, and implement `FromFormValue` so that it only validates integers over that age: @@ -554,10 +649,10 @@ The only condition is that the generic type in `JSON` implements the ### Streaming -Sometimes you just want to handle the incoming data directly. For example, you -might want to stream the incoming data out to a file. Rocket makes this as -simple as possible via the -[Data](https://api.rocket.rs/rocket/data/struct.Data.html) type: +Sometimes you just want to handle incoming data directly. For example, you might +want to stream the incoming data out to a file. Rocket makes this as simple as +possible via the [`Data`](https://api.rocket.rs/rocket/data/struct.Data.html) +type: ```rust #[post("/upload", format = "text/plain", data = "")] @@ -567,9 +662,9 @@ fn upload(data: Data) -> io::Result { ``` The route above accepts any `POST` request to the `/upload` path with -`Content-Type` `text/plain` The incoming data is streamed out to -`tmp/upload.txt` file, and the number of bytes written is returned as a plain -text response if the upload succeeds. If the upload fails, an error response is +`Content-Type: text/plain` The incoming data is streamed out to +`tmp/upload.txt`, and the number of bytes written is returned as a plain text +response if the upload succeeds. If the upload fails, an error response is returned. The handler above is complete. It really is that simple! See the [GitHub example code](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/raw_upload) @@ -638,7 +733,7 @@ Routing may fail for a variety of reasons. These include: * A handler returns a [`Responder`](/guide/responses/#responder) that fails. * No matching route was found. -If any of these conditions occurs, Rocket returns an error to the client. To do +If any of these conditions occur, Rocket returns an error to the client. To do so, Rocket invokes the _error catcher_ corresponding to the error's status code. A catcher is like a route, except it only handles errors. Catchers are declared via the `error` attribute, which takes a single integer corresponding to the @@ -660,10 +755,9 @@ rocket::ignite().catch(errors![not_found]) ``` Unlike request handlers, error handlers can only take 0, 1, or 2 parameters of -types [Request](https://api.rocket.rs/rocket/struct.Request.html) and/or -[Error](https://api.rocket.rs/rocket/enum.Error.html). At present, the `Error` -type is not particularly useful, and so it is often omitted. The -[error catcher +types [`Request`](https://api.rocket.rs/rocket/struct.Request.html) and/or +[`Error`](https://api.rocket.rs/rocket/enum.Error.html). At present, the `Error` +type is not particularly useful, and so it is often omitted. The [error catcher example](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/errors) on GitHub illustrates their use in full. diff --git a/site/guide/responses.md b/site/guide/responses.md index 2826946f..80d879bb 100644 --- a/site/guide/responses.md +++ b/site/guide/responses.md @@ -41,8 +41,7 @@ use rocket::response::status; #[post("/")] fn new(id: usize) -> status::Accepted { - let url = "http://example.com/resource.json"; - status::Created(url.into(), Some(format!("id: '{}'", id))) + status::Accepted(Some(format!("id: '{}'", id))) } ``` @@ -90,13 +89,13 @@ fn just_fail() -> Failure { } ``` -## `std` Implementations +## Implementations Rocket implements `Responder` for many types in Rust's standard library including `String`, `&str`, `File`, `Option`, and `Result`. The [`Responder`] documentation describes these in detail, but we briefly cover a few here. -### `&str` and `String` +### Strings The `Responder` implementations for `&str` and `String` are straight-forward: the string is used as a sized body, and the Content-Type of the response is set @@ -124,7 +123,7 @@ fn handler() -> &'static str { } ``` -### `Option` **where** `T: Responder` +### `Option` `Option` is _wrapping_ responder: an `Option` can only be returned when `T` implements `Responder`. If the `Option` is `Some`, the wrapped responder is used @@ -143,7 +142,7 @@ fn files(file: PathBuf) -> Option { } ``` -### `Result` **where** `E: Debug`, `E: Responder` +### `Result` `Result` is a special kind of wrapping responder: its functionality depends on whether the error type `E` implements `Responder`. @@ -194,8 +193,7 @@ many of these responders in the [`response`] module. Among these are: The `Stream` type deserves special attention. When a large amount of data needs to be sent to the client, it is better to stream the data to the client to avoid -consuming large amounts of memory. Rocket provides the -[Stream](https://api.rocket.rs/rocket/response/struct.Stream.html) type, making +consuming large amounts of memory. Rocket provides the [`Stream`] type, making this easy. The `Stream` type can be created from any `Read` type. For example, to stream from a local Unix stream, we might write: @@ -266,9 +264,9 @@ example, if a file ends with `.hbs`, Handlebars is used, while if a file ends with `.tera`, Tera is used. For templates to be properly registered, the template fairing must be attached -to the instance of Rocket. Fairings are explained in the next section. To attach -the template fairing, simply call `.attach(Template::fairing())` on an instance -of `Rocket` as follows: +to the instance of Rocket. The [Fairings](/guide/fairings) sections of the guide +provides more information on fairings. To attach the template fairing, simply +call `.attach(Template::fairing())` on an instance of `Rocket` as follows: ```rust fn main() { diff --git a/site/guide/state.md b/site/guide/state.md index 56df3e3a..e4a1c0b7 100644 --- a/site/guide/state.md +++ b/site/guide/state.md @@ -69,7 +69,7 @@ You can retrieve more than one `State` type in a single route as well: fn state(hit_count: State, config: State) -> T { ... } ``` -### Within a Request Guard +### Within Guards It can also be useful to retrieve managed state from a `FromRequest` implementation. To do so, simple invoke `State` as a guard using the @@ -124,7 +124,7 @@ warning: HitCount is not currently being managed by Rocket --> src/main.rs:2:17 | 2 | fn count(hit_count: State) -> String { - | ^^^^^^^^^ + | ^^^^^^^^ | = note: this State request guard will always fail help: maybe add a call to 'manage' here? @@ -143,9 +143,9 @@ globally, add `#![allow(unmanaged_state)]` to your crate attributes. You can find a complete example using the `HitCount` structure in the [state example on GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/state) and -learn more about the [manage +learn more about the [`manage` method](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage) and -[State type](https://api.rocket.rs/rocket/struct.State.html) in the API docs. +[`State` type](https://api.rocket.rs/rocket/struct.State.html) in the API docs. ## Databases @@ -175,7 +175,7 @@ detailed information on how to use Diesel, please see the [Diesel getting started guide](http://diesel.rs/guides/getting-started/). For this example, we use the following dependencies: -```toml +``` [dependencies] rocket = "0.2.8" diesel = { version = "*", features = ["sqlite"] } @@ -204,7 +204,7 @@ advocates for using a `DATABASE_URL` environment variable to set the database URL, and we use the same convention here. Excepting the long-winded types, the code is fairly straightforward: the `DATABASE_URL` environment variable is stored in the `DATABASE_URL` static, and an `r2d2::Pool` is created using the -default configuration parameter and a Diesel `SqliteConnection` +default configuration parameters and a Diesel `SqliteConnection` `ConnectionManager`. ```rust @@ -235,7 +235,7 @@ fn main() { } ``` -### Connection Request Guard +### Connection Guard The second and final step is to implement a request guard that retrieves a single connection from the managed connection pool. We create a new type, diff --git a/site/guide/testing.md b/site/guide/testing.md index 3515c1dd..15d4822a 100644 --- a/site/guide/testing.md +++ b/site/guide/testing.md @@ -14,27 +14,27 @@ instance. Usage is straightforward: 1. Construct a `Rocket` instance that represents the application. - ```rust - let rocket = rocket::ignite(); - ``` + ```rust + let rocket = rocket::ignite(); + ``` 2. Construct a `Client` using the `Rocket` instance. - ```rust - let client = Client::new(rocket).expect("valid rocket instance"); - ``` + ```rust + let client = Client::new(rocket).expect("valid rocket instance"); + ``` 3. Construct requests using the `Client` instance. - ```rust - let req = client.get("/"); - ``` + ```rust + let req = client.get("/"); + ``` - 3. Dispatch the request to retrieve the response. + 4. Dispatch the request to retrieve the response. - ```rust - let response = req.dispatch(); - ``` + ```rust + let response = req.dispatch(); + ``` [`local`]: https://api.rocket.rs/rocket/local/index.html [`Client`]: https://api.rocket.rs/rocket/local/struct.Client.html diff --git a/site/index.toml b/site/index.toml index 66cd7525..73cd6531 100644 --- a/site/index.toml +++ b/site/index.toml @@ -141,9 +141,9 @@ color = 'blue' [[bottom_features]] title = 'Cookies' -text = "Cookies are first-class in Rocket. View, add, or remove cookies without hassle." +text = "View, add, or remove cookies, with or without encryption, without hassle." image = 'cookies-icon' -url = '/guide/requests/#request-guards' +url = '/guide/requests/#cookies' button = 'Learn More' color = 'purple' margin = -6 @@ -161,14 +161,14 @@ margin = -29 title = 'Config Environments' text = "Configure your application your way for development, staging, and production." image = 'config-icon' -url = '/guide/overview/#configuration' +url = '/guide/configuration/#environment' button = 'Learn More' color = 'yellow' margin = -3 [[bottom_features]] -title = 'Query Params' -text = "Handling query parameters isn’t an afterthought in Rocket." +title = 'Query Strings' +text = "Handling query strings and parameters is type-safe and easy in Rocket." image = 'query-icon' url = '/guide/requests/#query-strings' button = 'Learn More' diff --git a/site/news.toml b/site/news.toml index bcf11e14..abb37851 100644 --- a/site/news.toml +++ b/site/news.toml @@ -1,5 +1,7 @@ [[articles]] title = "Rocket v0.2: Managed State & More" +author = "Sergio Benitez" +author_url = "https://sergio.bz" date = "February 06, 2017" snippet = """ Today marks the first major release since Rocket's debut a little over a month @@ -7,7 +9,7 @@ ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements. Much of the development in v0.2 was led by the community, either through reports via the [GitHub issue tracker](https://github.com/SergioBenitez/Rocket/issues) or via direct contributions. In fact, there have been **20 unique contributors** -to Rocket's code since Rocket's initial introduction! Community feedback has +to Rocket's codebase since Rocket's initial introduction! Community feedback has been incredible. As a special thank you, we include the names of these contributors at the end of this article. """ diff --git a/site/news/2017-02-06-version-0.2.md b/site/news/2017-02-06-version-0.2.md index 2bfd87c0..2a4a83fd 100644 --- a/site/news/2017-02-06-version-0.2.md +++ b/site/news/2017-02-06-version-0.2.md @@ -1,6 +1,10 @@ # Rocket v0.2: Managed State & More -**Posted by [Sergio Benitez](https://sergio.bz) on February 06, 2017** + + + Today marks the first major release since Rocket's debut a little over a month ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements.