mirror of https://github.com/rwf2/Rocket.git
Update and fix guide tests.
This commit is contained in:
parent
08b34e8263
commit
2c82b3e1d5
|
@ -268,13 +268,13 @@ Here's our version (in `src/main.rs`):
|
|||
# fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
|
||||
# }
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rocket::Data;
|
||||
use rocket::response::Debug;
|
||||
|
||||
#[post("/", data = "<paste>")]
|
||||
fn upload(paste: Data) -> Result<String, Debug<std::io::Error>> {
|
||||
async fn upload(paste: Data) -> Result<String, Debug<std::io::Error>> {
|
||||
let id = PasteId::new(3);
|
||||
let filename = format!("upload/{id}", id = id);
|
||||
let url = format!("{host}/{id}\n", host = "http://localhost:8000", id = id);
|
||||
|
|
|
@ -259,11 +259,14 @@ stalls, or sometimes even deadlocks can occur.
|
|||
`tokio::task::spawn_blocking`:
|
||||
|
||||
```rust
|
||||
# #[macro_use] extern crate rocket;
|
||||
use rocket::tokio::task::spawn_blocking;
|
||||
|
||||
#[get("/blocking_task")]
|
||||
async fn blocking_task() -> String {
|
||||
async fn blocking_task() -> Vec<u8> {
|
||||
// In a real application, we would use rocket::response::NamedFile
|
||||
tokio::task::spawn_blocking(|| {
|
||||
std::fs::read_file("data.txt")
|
||||
}).await
|
||||
spawn_blocking(|| {
|
||||
std::fs::read("data.txt").expect("failed to read file")
|
||||
}).await.unwrap()
|
||||
}
|
||||
```
|
||||
|
|
|
@ -166,8 +166,8 @@ this, a safe and secure static file server can be implemented in 4 lines:
|
|||
use rocket::response::NamedFile;
|
||||
|
||||
#[get("/<file..>")]
|
||||
fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||
async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).await.ok()
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1058,8 +1058,8 @@ use rocket::Data;
|
|||
use rocket::response::Debug;
|
||||
|
||||
#[post("/upload", format = "plain", data = "<data>")]
|
||||
fn upload(data: Data) -> Result<String, Debug<std::io::Error>> {
|
||||
Ok(data.stream_to_file("/tmp/upload.txt").map(|n| n.to_string())?)
|
||||
async fn upload(data: Data) -> Result<String, Debug<std::io::Error>> {
|
||||
Ok(data.stream_to_file("/tmp/upload.txt").await.map(|n| n.to_string())?)
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1083,24 +1083,18 @@ returned. The handler above is complete. It really is that simple! See the
|
|||
Rocket makes it easy to use `async/await` in routes.
|
||||
|
||||
```rust
|
||||
#[get("/weather")]
|
||||
async fn weather() -> String {
|
||||
let response = reqwest::get("https://www.example.com").await;
|
||||
response.text().await
|
||||
# #[macro_use] extern crate rocket;
|
||||
use rocket::tokio::time::{delay_for, Duration};
|
||||
#[get("/delay/<seconds>")]
|
||||
async fn delay(seconds: u64) -> String {
|
||||
delay_for(Duration::from_secs(seconds)).await;
|
||||
format!("Waited for {} seconds", seconds)
|
||||
}
|
||||
```
|
||||
|
||||
First, notice that the route function is an `async fn`. This enables
|
||||
the use of `await` inside the handler. `reqwest` is an asynchronous
|
||||
HTTP client, so we must `await` the response. Finally, we call
|
||||
the `text()` function, which asynchronously downloads the full
|
||||
response data from the server.
|
||||
|
||||
! warning: You should _always_ set limits when reading incoming data.
|
||||
|
||||
Just like with client input, you should usually limit the amount
|
||||
of data read from external APIs. The exact method will depend
|
||||
on the library you are using to make requests.
|
||||
the use of `await` inside the handler. `delay_for` is an asynchronous
|
||||
function, so we must `await` it.
|
||||
|
||||
## Error Catchers
|
||||
|
||||
|
|
|
@ -185,14 +185,15 @@ use rocket::response::{self, Response, Responder};
|
|||
use rocket::http::ContentType;
|
||||
|
||||
# struct String(std::string::String);
|
||||
impl<'a> Responder<'a> for String {
|
||||
fn respond_to(self, _: &Request) -> response::Result<'a> {
|
||||
#[rocket::async_trait]
|
||||
impl<'r> Responder<'r, 'static> for String {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
# /*
|
||||
.sized_body(Cursor::new(self))
|
||||
.sized_body(self.len(), Cursor::new(self))
|
||||
# */
|
||||
# .sized_body(Cursor::new(self.0))
|
||||
# .sized_body(self.0.len(), Cursor::new(self.0))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
@ -230,8 +231,8 @@ found and a `404` when a file is not found in just 4, idiomatic lines:
|
|||
use rocket::response::NamedFile;
|
||||
|
||||
#[get("/<file..>")]
|
||||
fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||
async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).await.ok()
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -256,9 +257,9 @@ use rocket::response::NamedFile;
|
|||
use rocket::response::status::NotFound;
|
||||
|
||||
#[get("/<file..>")]
|
||||
fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
|
||||
async fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
|
||||
let path = Path::new("static/").join(file);
|
||||
NamedFile::open(&path).map_err(|e| NotFound(e.to_string()))
|
||||
NamedFile::open(&path).await.map_err(|e| NotFound(e.to_string()))
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -306,14 +307,17 @@ example, to stream from a local Unix stream, we might write:
|
|||
# #[macro_use] extern crate rocket;
|
||||
# fn main() {}
|
||||
|
||||
# #[cfg(unix)]
|
||||
# mod test {
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use rocket::response::{Stream, Debug};
|
||||
|
||||
use rocket::tokio::net::TcpStream;
|
||||
|
||||
#[get("/stream")]
|
||||
fn stream() -> Result<Stream<UnixStream>, Debug<std::io::Error>> {
|
||||
Ok(UnixStream::connect("/path/to/my/socket").map(Stream::from)?)
|
||||
async fn stream() -> Result<Stream<TcpStream>, Debug<std::io::Error>> {
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 9999));
|
||||
Ok(TcpStream::connect(addr).await.map(Stream::from)?)
|
||||
}
|
||||
# }
|
||||
```
|
||||
|
|
|
@ -127,11 +127,12 @@ use rocket::request::{self, Request, FromRequest};
|
|||
# struct T;
|
||||
# struct HitCount { count: AtomicUsize }
|
||||
# type ErrorType = ();
|
||||
#[rocket::async_trait]
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for T {
|
||||
type Error = ErrorType;
|
||||
|
||||
fn from_request(req: &'a Request<'r>) -> request::Outcome<T, Self::Error> {
|
||||
let hit_count_state = try_outcome!(req.guard::<State<HitCount>>());
|
||||
async fn from_request(req: &'a Request<'r>) -> request::Outcome<T, Self::Error> {
|
||||
let hit_count_state = try_outcome!(req.guard::<State<HitCount>>().await);
|
||||
let current_count = hit_count_state.count.load(Ordering::Relaxed);
|
||||
/* ... */
|
||||
# request::Outcome::Success(T)
|
||||
|
@ -171,10 +172,11 @@ static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|||
struct RequestId(pub usize);
|
||||
|
||||
/// Returns the current request's ID, assigning one only as necessary.
|
||||
#[rocket::async_trait]
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for &'a RequestId {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
||||
async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
||||
// The closure passed to `local_cache` will be executed at most once per
|
||||
// request: the first time the `RequestId` guard is used. If it is
|
||||
// requested again, `local_cache` will return the same value.
|
||||
|
|
|
@ -49,8 +49,8 @@ example, the following snippet attached two fairings, `req_fairing` and
|
|||
`res_fairing`, to a new Rocket instance:
|
||||
|
||||
```rust
|
||||
# let req_fairing = rocket::fairing::AdHoc::on_request("example", |_, _| {});
|
||||
# let res_fairing = rocket::fairing::AdHoc::on_response("example", |_, _| {});
|
||||
# let req_fairing = rocket::fairing::AdHoc::on_request("example", |_, _| Box::pin(async {}));
|
||||
# let res_fairing = rocket::fairing::AdHoc::on_response("example", |_, _| Box::pin(async {}));
|
||||
|
||||
# if false {
|
||||
rocket::ignite()
|
||||
|
@ -157,6 +157,7 @@ struct Counter {
|
|||
post: AtomicUsize,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl Fairing for Counter {
|
||||
// This is a request and response fairing named "GET/POST Counter".
|
||||
fn info(&self) -> Info {
|
||||
|
@ -167,7 +168,7 @@ impl Fairing for Counter {
|
|||
}
|
||||
|
||||
// Increment the counter for `GET` and `POST` requests.
|
||||
fn on_request(&self, request: &mut Request, _: &Data) {
|
||||
async fn on_request<'a>(&'a self, request: &'a mut Request<'_>, _: &'a Data) {
|
||||
match request.method() {
|
||||
Method::Get => self.get.fetch_add(1, Ordering::Relaxed),
|
||||
Method::Post => self.post.fetch_add(1, Ordering::Relaxed),
|
||||
|
@ -175,24 +176,22 @@ impl Fairing for Counter {
|
|||
};
|
||||
}
|
||||
|
||||
fn on_response<'a>(&'a self, request: &'a Request<'_>, response: &'a mut Response<'_>) -> BoxFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
// Don't change a successful user's response, ever.
|
||||
if response.status() != Status::NotFound {
|
||||
return
|
||||
}
|
||||
async fn on_response<'a>(&'a self, request: &'a Request<'_>, response: &'a mut Response<'_>) {
|
||||
// Don't change a successful user's response, ever.
|
||||
if response.status() != Status::NotFound {
|
||||
return
|
||||
}
|
||||
|
||||
// Rewrite the response to return the current counts.
|
||||
if request.method() == Method::Get && request.uri().path() == "/counts" {
|
||||
let get_count = self.get.load(Ordering::Relaxed);
|
||||
let post_count = self.post.load(Ordering::Relaxed);
|
||||
let body = format!("Get: {}\nPost: {}", get_count, post_count);
|
||||
// Rewrite the response to return the current counts.
|
||||
if request.method() == Method::Get && request.uri().path() == "/counts" {
|
||||
let get_count = self.get.load(Ordering::Relaxed);
|
||||
let post_count = self.post.load(Ordering::Relaxed);
|
||||
let body = format!("Get: {}\nPost: {}", get_count, post_count);
|
||||
|
||||
response.set_status(Status::Ok);
|
||||
response.set_header(ContentType::Plain);
|
||||
response.set_sized_body(Cursor::new(body));
|
||||
}
|
||||
})
|
||||
response.set_status(Status::Ok);
|
||||
response.set_header(ContentType::Plain);
|
||||
response.set_sized_body(body.len(), Cursor::new(body));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -22,7 +22,7 @@ instance. Usage is straightforward:
|
|||
2. Construct a `Client` using the `Rocket` instance.
|
||||
|
||||
```rust
|
||||
# use rocket::local::Client;
|
||||
# use rocket::local::blocking::Client;
|
||||
# let rocket = rocket::ignite();
|
||||
let client = Client::new(rocket).expect("valid rocket instance");
|
||||
# let _ = client;
|
||||
|
@ -31,7 +31,7 @@ instance. Usage is straightforward:
|
|||
3. Construct requests using the `Client` instance.
|
||||
|
||||
```rust
|
||||
# use rocket::local::Client;
|
||||
# use rocket::local::blocking::Client;
|
||||
# let rocket = rocket::ignite();
|
||||
# let client = Client::new(rocket).unwrap();
|
||||
let req = client.get("/");
|
||||
|
@ -41,7 +41,7 @@ instance. Usage is straightforward:
|
|||
4. Dispatch the request to retrieve the response.
|
||||
|
||||
```rust
|
||||
# use rocket::local::Client;
|
||||
# use rocket::local::blocking::Client;
|
||||
# let rocket = rocket::ignite();
|
||||
# let client = Client::new(rocket).unwrap();
|
||||
# let req = client.get("/");
|
||||
|
@ -94,11 +94,11 @@ These methods are typically used in combination with the `assert_eq!` or
|
|||
# Response::build()
|
||||
# .header(ContentType::Plain)
|
||||
# .header(Header::new("X-Special", ""))
|
||||
# .sized_body(Cursor::new("Expected Body"))
|
||||
# .sized_body("Expected Body".len(), Cursor::new("Expected Body"))
|
||||
# .finalize()
|
||||
# }
|
||||
|
||||
use rocket::local::Client;
|
||||
use rocket::local::blocking::Client;
|
||||
use rocket::http::{ContentType, Status};
|
||||
|
||||
let rocket = rocket::ignite().mount("/", routes![hello]);
|
||||
|
@ -108,7 +108,7 @@ let mut response = client.get("/").dispatch();
|
|||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::Plain));
|
||||
assert!(response.headers().get_one("X-Special").is_some());
|
||||
assert_eq!(response.body_string(), Some("Expected Body".into()));
|
||||
assert_eq!(response.into_string(), Some("Expected Body".into()));
|
||||
```
|
||||
|
||||
## Testing "Hello, world!"
|
||||
|
@ -173,7 +173,7 @@ testing: we _want_ our tests to panic when something goes wrong.
|
|||
|
||||
```rust
|
||||
# fn rocket() -> rocket::Rocket { rocket::ignite() }
|
||||
# use rocket::local::Client;
|
||||
# use rocket::local::blocking::Client;
|
||||
|
||||
let client = Client::new(rocket()).expect("valid rocket instance");
|
||||
```
|
||||
|
@ -183,7 +183,7 @@ application's response:
|
|||
|
||||
```rust
|
||||
# fn rocket() -> rocket::Rocket { rocket::ignite() }
|
||||
# use rocket::local::Client;
|
||||
# use rocket::local::blocking::Client;
|
||||
# let client = Client::new(rocket()).expect("valid rocket instance");
|
||||
let mut response = client.get("/").dispatch();
|
||||
```
|
||||
|
@ -203,7 +203,7 @@ We do this by checking the `Response` object directly:
|
|||
# #[get("/")]
|
||||
# fn hello() -> &'static str { "Hello, world!" }
|
||||
|
||||
# use rocket::local::Client;
|
||||
# use rocket::local::blocking::Client;
|
||||
use rocket::http::{ContentType, Status};
|
||||
#
|
||||
# let rocket = rocket::ignite().mount("/", routes![hello]);
|
||||
|
|
|
@ -201,15 +201,15 @@ use rocket::fairing::AdHoc;
|
|||
struct AssetsDir(String);
|
||||
|
||||
#[get("/<asset..>")]
|
||||
fn assets(asset: PathBuf, assets_dir: State<AssetsDir>) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new(&assets_dir.0).join(asset)).ok()
|
||||
async fn assets(asset: PathBuf, assets_dir: State<'_, AssetsDir>) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new(&assets_dir.0).join(asset)).await.ok()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
# if false {
|
||||
rocket::ignite()
|
||||
.mount("/", routes![assets])
|
||||
.attach(AdHoc::on_attach("Assets Config", |mut rocket| {
|
||||
.attach(AdHoc::on_attach("Assets Config", |mut rocket| async {
|
||||
let assets_dir = rocket.config().await
|
||||
.get_str("assets_dir")
|
||||
.unwrap_or("assets/")
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// #![feature(external_doc)]
|
||||
#![feature(external_doc)]
|
||||
|
||||
// rocket::rocket_internal_guide_tests!("../guide/*.md");
|
||||
rocket::rocket_internal_guide_tests!("../guide/*.md");
|
||||
|
|
Loading…
Reference in New Issue