1
0
mirror of https://github.com/actix/actix-website synced 2025-02-21 11:54:48 +01:00

update url-dispatch examples

This commit is contained in:
Nikolay Kim 2018-05-24 10:13:55 -07:00
parent 2efb3dc0bd
commit 842c877da2
19 changed files with 348 additions and 249 deletions

View File

@ -30,20 +30,7 @@ routing table. This method accepts a *path pattern*,
*http method* and a handler function. `route()` method could be called multiple times *http method* and a handler function. `route()` method could be called multiple times
for the same path, in that case, multiple routes register for the same resource path. for the same path, in that case, multiple routes register for the same resource path.
```rust {{< include-example example="url-dispatch" section="main" >}}
use actix_web::{App, HttpRequest, HttpResponse, http::Method};
fn index(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.route("/user/{name}", Method::GET, index)
.route("/user/{name}", Method::POST, index)
.finish();
}
```
While *App::route()* provides simple way of registering routes, to access While *App::route()* provides simple way of registering routes, to access
complete resource configuration, different method has to be used. complete resource configuration, different method has to be used.
@ -51,21 +38,7 @@ The [*App::resource()*](../../actix-web/actix_web/struct.App.html#method.resourc
adds a single resource to application routing table. This method accepts a *path pattern* adds a single resource to application routing table. This method accepts a *path pattern*
and a resource configuration function. and a resource configuration function.
```rust {{< include-example example="url-dispatch" file="resource.rs" section="resource" >}}
use actix_web::{App, HttpRequest, HttpResponse, http::Method};
fn index(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.resource("/prefix", |r| r.f(index))
.resource("/user/{name}",
|r| r.method(Method::GET).f(|req| HttpResponse::Ok()))
.finish();
}
```
The *Configuration function* has the following type: The *Configuration function* has the following type:
@ -90,18 +63,7 @@ the order the routes were registered via `Resource::route()`.
> A *Route* can contain any number of *predicates* but only one handler. > A *Route* can contain any number of *predicates* but only one handler.
```rust {{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
fn main() {
App::new()
.resource("/path", |resource|
resource.route()
.filter(pred::Get())
.filter(pred::Header("content-type", "text/plain"))
.f(|req| HttpResponse::Ok())
)
.finish();
}
```
In this example, `HttpResponse::Ok()` is returned for *GET* requests. In this example, `HttpResponse::Ok()` is returned for *GET* requests.
If a request contains `Content-Type` header, the value of this header is *text/plain*, If a request contains `Content-Type` header, the value of this header is *text/plain*,
@ -301,37 +263,10 @@ consisting of "Tasks". Such paths may include:
A scoped layout of these paths would appear as follows A scoped layout of these paths would appear as follows
```rust {{< include-example example="url-dispatch" file="scope.rs" section="scope" >}}
App::new()
.scope("/project", |proj_scope| {
proj_scope
.resource("", |r| {
r.method(Method::GET)
.f(get_projects);
r.method(Method::POST)
.f(create_project)})
.resource("/{project_id}", |r| {
r.method(Method::PUT)
.with(update_project);
r.method(Method::DELETE)
.f(delete_project)})
.nested("/{project_id}/task", |task_scope| {
task_scope
.resource("", |r| {
r.method(Method::GET)
.f(get_tasks);
r.method(Method::POST)
.f(create_task)})
.resource("/{task_id}", |r| {
r.method(Method::PUT)
.with(update_task);
r.method(Method::DELETE)
.with(delete_task)})})})
```
A *scoped* path can contain variable path segments as resources. Consistent with A *scoped* path can contain variable path segments as resources. Consistent with
unscoped paths, a scoped prefix without a trailing slash has one automatically unscoped paths.
appended to it: `/app` converts to `/app/`.
You can get variable path segments from `HttpRequest::match_info()`. You can get variable path segments from `HttpRequest::match_info()`.
`Path` extractor also is able to extract scope level variable segments. `Path` extractor also is able to extract scope level variable segments.
@ -347,21 +282,7 @@ Any matched parameter can be deserialized into a specific type if the type
implements the `FromParam` trait. For example most standard integer types implements the `FromParam` trait. For example most standard integer types
the trait, i.e.: the trait, i.e.:
```rust {{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}}
use actix_web::*;
fn index(req: HttpRequest) -> Result<String> {
let v1: u8 = req.match_info().query("v1")?;
let v2: u8 = req.match_info().query("v2")?;
Ok(format!("Values {} {}", v1, v2))
}
fn main() {
App::new()
.resource(r"/a/{v1}/{v2}/", |r| r.f(index))
.finish();
}
```
For this example for path '/a/1/2/', values v1 and v2 will resolve to "1" and "2". For this example for path '/a/1/2/', values v1 and v2 will resolve to "1" and "2".
@ -381,21 +302,7 @@ an `Err` is returned indicating the condition met:
As a result of these conditions, a `PathBuf` parsed from request path parameter is As a result of these conditions, a `PathBuf` parsed from request path parameter is
safe to interpolate within, or use as a suffix of, a path without additional checks. safe to interpolate within, or use as a suffix of, a path without additional checks.
```rust {{< include-example example="url-dispatch" file="pbuf.rs" section="pbuf" >}}
use std::path::PathBuf;
use actix_web::{App, HttpRequest, Result, http::Method};
fn index(req: HttpRequest) -> Result<String> {
let path: PathBuf = req.match_info().query("tail")?;
Ok(format!("Path {:?}", path))
}
fn main() {
App::new()
.resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index))
.finish();
}
```
List of `FromParam` implementations can be found in List of `FromParam` implementations can be found in
[api docs](../../actix-web/actix_web/dev/trait.FromParam.html#foreign-impls) [api docs](../../actix-web/actix_web/dev/trait.FromParam.html#foreign-impls)
@ -410,45 +317,12 @@ path pattern. i.e. you can match path pattern `/{id}/{username}/` against
`Pyth<(u32, String)>` type, but `Path<(String, String, String)>` type will `Pyth<(u32, String)>` type, but `Path<(String, String, String)>` type will
always fail. always fail.
```rust {{< include-example example="url-dispatch" file="path.rs" section="path" >}}
use actix_web::{App, Path, Result, http::Method};
// extract path info using serde
fn index(info: Path<(String, u32)>) -> Result<String> {
Ok(format!("Welcome {}! id: {}", info.0, info.1))
}
fn main() {
let app = App::new()
.resource("/{username}/{id}/index.html", // <- define path parameters
|r| r.method(Method::GET).with(index));
}
```
It also possible to extract path pattern information to a struct. In this case, It also possible to extract path pattern information to a struct. In this case,
this struct must implement *serde's *`Deserialize` trait. this struct must implement *serde's *`Deserialize` trait.
```rust {{< include-example example="url-dispatch" file="path2.rs" section="path" >}}
#[macro_use] extern crate serde_derive;
use actix_web::{App, Path, Result, http::Method};
#[derive(Deserialize)]
struct Info {
username: String,
}
// extract path info using serde
fn index(info: Path<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new()
.resource("/{username}/index.html", // <- define path parameters
|r| r.method(Method::GET).with(index));
}
```
[*Query*](../../actix-web/actix_web/struct.Query.html) provides similar [*Query*](../../actix-web/actix_web/struct.Query.html) provides similar
functionality for request query parameters. functionality for request query parameters.
@ -459,24 +333,7 @@ Use the [*HttpRequest.url_for()*](../../actix-web/actix_web/struct.HttpRequest.h
method to generate URLs based on resource patterns. For example, if you've configured a method to generate URLs based on resource patterns. For example, if you've configured a
resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this: resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this:
```rust {{< include-example example="url-dispatch" file="urls.rs" section="url" >}}
fn index(req: HttpRequest) -> Result<HttpResponse> {
let url = req.url_for("foo", &["1", "2", "3"])?; // <- generate url for "foo" resource
Ok(HttpResponse::Found()
.header(header::LOCATION, url.as_str())
.finish())
}
fn main() {
let app = App::new()
.resource("/test/{a}/{b}/{c}", |r| {
r.name("foo"); // <- set resource name, then it could be used in `url_for`
r.method(Method::GET).f(|_| HttpResponse::Ok());
})
.route("/test/", Method::GET, index)
.finish();
}
```
This would return something like the string *http://example.com/test/1/2/3* (at least if This would return something like the string *http://example.com/test/1/2/3* (at least if
the current protocol and hostname implied http://example.com). the current protocol and hostname implied http://example.com).
@ -489,23 +346,7 @@ can modify this url (add query parameters, anchor, etc).
Resources that are valid URLs, can be registered as external resources. They are useful Resources that are valid URLs, can be registered as external resources. They are useful
for URL generation purposes only and are never considered for matching at request time. for URL generation purposes only and are never considered for matching at request time.
```rust {{< include-example example="url-dispatch" file="url_ext.rs" section="ext" >}}
# extern crate actix_web;
use actix_web::{App, HttpRequest, HttpResponse, Error};
fn index(mut req: HttpRequest) -> Result<HttpResponse, Error> {
let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
Ok(HttpResponse::Ok().into())
}
fn main() {
let app = App::new()
.resource("/index.html", |r| r.f(index))
.external_resource("youtube", "https://youtube.com/watch/{video_id}")
.finish();
}
```
# Path normalization and redirecting to slash-appended routes # Path normalization and redirecting to slash-appended routes
@ -527,16 +368,7 @@ If *merge* is *true*, merge multiple consecutive slashes in the path into one.
This handler designed to be used as a handler for application's *default resource*. This handler designed to be used as a handler for application's *default resource*.
```rust {{< include-example example="url-dispatch" file="norm.rs" section="norm" >}}
use actix_web::http::NormalizePath;
fn main() {
let app = App::new()
.resource("/resource/", |r| r.f(index))
.default_resource(|r| r.h(NormalizePath::default()))
.finish();
}
```
In this example `/resource`, `//resource///` will be redirected to `/resource/`. In this example `/resource`, `//resource///` will be redirected to `/resource/`.
@ -547,16 +379,7 @@ slash-appending *Not Found* will turn a *POST* request into a GET, losing any
It is possible to register path normalization only for *GET* requests only: It is possible to register path normalization only for *GET* requests only:
```rust {{< include-example example="url-dispatch" file="norm2.rs" section="norm" >}}
use actix_web::{App, HttpRequest, http::Method, http::NormalizePath};
fn main() {
let app = App::new()
.resource("/resource/", |r| r.f(index))
.default_resource(|r| r.method(Method::GET).h(NormalizePath::default()))
.finish();
}
```
## Using an Application Prefix to Compose Applications ## Using an Application Prefix to Compose Applications
@ -568,18 +391,7 @@ resource names.
For example: For example:
```rust {{< include-example example="url-dispatch" file="prefix.rs" section="prefix" >}}
fn show_users(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.prefix("/users")
.resource("/show", |r| r.f(show_users))
.finish();
}
```
In the above example, the *show_users* route will have an effective route pattern of In the above example, the *show_users* route will have an effective route pattern of
*/users/show* instead of */show* because the application's prefix argument will be prepended */users/show* instead of */show* because the application's prefix argument will be prepended
@ -597,26 +409,7 @@ several predicates, you can check
Here is a simple predicate that check that a request contains a specific *header*: Here is a simple predicate that check that a request contains a specific *header*:
```rust {{< include-example example="url-dispatch" file="pred.rs" section="pred" >}}
use actix_web::{http, pred::Predicate, App, HttpMessage, HttpRequest};
struct ContentTypeHeader;
impl<S: 'static> Predicate<S> for ContentTypeHeader {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
req.headers().contains_key(http::header::CONTENT_TYPE)
}
}
fn main() {
App::new()
.resource("/index.html", |r|
r.route()
.filter(ContentTypeHeader)
.f(|_| HttpResponse::Ok()));
}
```
In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header. In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header.
@ -630,18 +423,7 @@ You can invert the meaning of any predicate value by wrapping it in a `Not` pred
For example, if you want to return "METHOD NOT ALLOWED" response for all methods For example, if you want to return "METHOD NOT ALLOWED" response for all methods
except "GET": except "GET":
```rust {{< include-example example="url-dispatch" file="pred2.rs" section="pred" >}}
use actix_web::{pred, App, HttpResponse};
fn main() {
App::new()
.resource("/index.html", |r|
r.route()
.filter(pred::Not(pred::Get()))
.f(|req| HttpResponse::MethodNotAllowed()))
.finish();
}
```
The `Any` predicate accepts a list of predicates and matches if any of the supplied The `Any` predicate accepts a list of predicates and matches if any of the supplied
predicates match. i.e: predicates match. i.e:
@ -665,16 +447,4 @@ It is possible to override the *NOT FOUND* response with `App::default_resource(
This method accepts a *configuration function* same as normal resource configuration This method accepts a *configuration function* same as normal resource configuration
with `App::resource()` method. with `App::resource()` method.
```rust {{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}}
use actix_web::{App, HttpResponse, http::Method, pred};
fn main() {
App::new()
.default_resource(|r| {
r.method(Method::GET).f(|req| HttpResponse::NotFound());
r.route().filter(pred::Not(pred::Get()))
.f(|req| HttpResponse::MethodNotAllowed());
})
.finish();
}
```

View File

@ -3,4 +3,5 @@ members = [
"application", "application",
"getting-started", "getting-started",
"server", "server",
"url-dispatch",
] ]

View File

@ -0,0 +1,12 @@
[package]
name = "url-dispatch"
version = "0.6.0"
workspace = "../"
[dependencies]
actix = "0.5"
actix-web = "0.6"
futures = "0.1"
openssl = "0.10"
serde = "1.0"
serde_derive = "1.0"

View File

@ -0,0 +1,15 @@
// <cfg>
use actix_web::{pred, App, HttpResponse};
fn main() {
App::new()
.resource("/path", |resource| {
resource
.route()
.filter(pred::Get())
.filter(pred::Header("content-type", "text/plain"))
.f(|req| HttpResponse::Ok())
})
.finish();
}
// </cfg>

View File

@ -0,0 +1,14 @@
// <default>
use actix_web::{http::Method, pred, App, HttpResponse};
fn main() {
App::new()
.default_resource(|r| {
r.method(Method::GET).f(|req| HttpResponse::NotFound());
r.route()
.filter(pred::Not(pred::Get()))
.f(|req| HttpResponse::MethodNotAllowed());
})
.finish();
}
// </default>

View File

@ -0,0 +1,38 @@
extern crate actix;
extern crate actix_web;
extern crate futures;
extern crate openssl;
#[macro_use]
extern crate serde_derive;
extern crate serde;
mod cfg;
mod dhandler;
mod minfo;
mod norm;
mod norm2;
mod path;
mod path2;
mod pbuf;
mod pred;
mod pred2;
mod prefix;
mod resource;
mod scope;
mod url_ext;
mod urls;
// <main>
use actix_web::{http::Method, App, HttpRequest, HttpResponse};
fn index(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.route("/user/{name}", Method::GET, index)
.route("/user/{name}", Method::POST, index)
.finish();
}
// </main>

View File

@ -0,0 +1,15 @@
// <minfo>
use actix_web::{App, HttpRequest, Result};
fn index(req: HttpRequest) -> Result<String> {
let v1: u8 = req.match_info().query("v1")?;
let v2: u8 = req.match_info().query("v2")?;
Ok(format!("Values {} {}", v1, v2))
}
fn main() {
App::new()
.resource(r"/a/{v1}/{v2}/", |r| r.f(index))
.finish();
}
// </minfo>

View File

@ -0,0 +1,15 @@
// <norm>
use actix_web::{http::NormalizePath, App};
fn main() {
let app = App::new()
.resource("/resource/", |r| r.f(index))
.default_resource(|r| r.h(NormalizePath::default()))
.finish();
}
// </norm>
use actix_web::HttpRequest;
fn index(req: HttpRequest) -> String {
unimplemented!()
}

View File

@ -0,0 +1,16 @@
// <norm>
use actix_web::{http::Method, http::NormalizePath, App};
fn main() {
let app = App::new()
.resource("/resource/", |r| r.f(index))
.default_resource(|r| r.method(Method::GET).h(NormalizePath::default()))
.finish();
}
// </norm>
use actix_web::HttpRequest;
fn index(req: HttpRequest) -> String {
unimplemented!()
}

View File

@ -0,0 +1,15 @@
// <path>
use actix_web::{http::Method, App, Path, Result};
// extract path info using serde
fn index(info: Path<(String, u32)>) -> Result<String> {
Ok(format!("Welcome {}! id: {}", info.0, info.1))
}
fn main() {
let app = App::new().resource(
"/{username}/{id}/index.html", // <- define path parameters
|r| r.method(Method::GET).with(index),
);
}
// </path>

View File

@ -0,0 +1,21 @@
// <path>
extern crate serde_derive;
use actix_web::{http::Method, App, Path, Result};
#[derive(Deserialize)]
struct Info {
username: String,
}
// extract path info using serde
fn index(info: Path<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/{username}/index.html", // <- define path parameters
|r| r.method(Method::GET).with(index),
);
}
// </path>

View File

@ -0,0 +1,15 @@
// <pbuf>
use actix_web::{http::Method, App, HttpRequest, Result};
use std::path::PathBuf;
fn index(req: HttpRequest) -> Result<String> {
let path: PathBuf = req.match_info().query("tail")?;
Ok(format!("Path {:?}", path))
}
fn main() {
App::new()
.resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index))
.finish();
}
// </pbuf>

View File

@ -0,0 +1,19 @@
// <pred>
use actix_web::{http, pred::Predicate, App, HttpMessage, HttpRequest, HttpResponse};
struct ContentTypeHeader;
impl<S: 'static> Predicate<S> for ContentTypeHeader {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
req.headers().contains_key(http::header::CONTENT_TYPE)
}
}
fn main() {
App::new().resource("/index.html", |r| {
r.route()
.filter(ContentTypeHeader)
.f(|_| HttpResponse::Ok())
});
}
// </pred>

View File

@ -0,0 +1,13 @@
// <pred>
use actix_web::{pred, App, HttpResponse};
fn main() {
App::new()
.resource("/index.html", |r| {
r.route()
.filter(pred::Not(pred::Get()))
.f(|req| HttpResponse::MethodNotAllowed())
})
.finish();
}
// </pred>

View File

@ -0,0 +1,14 @@
use actix_web::{App, HttpRequest, HttpResponse};
// <prefix>
fn show_users(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.prefix("/users")
.resource("/show", |r| r.f(show_users))
.finish();
}
// </prefix>

View File

@ -0,0 +1,16 @@
// <resource>
use actix_web::{http::Method, App, HttpRequest, HttpResponse};
fn index(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.resource("/prefix", |r| r.f(index))
.resource("/user/{name}", |r| {
r.method(Method::GET).f(|req| HttpResponse::Ok())
})
.finish();
}
// </resource>

View File

@ -0,0 +1,54 @@
#![allow(dead_code)]
use actix_web::{http::Method, App, HttpRequest};
fn get_projects(_: HttpRequest) -> String {
unimplemented!()
}
fn create_project(_: HttpRequest) -> String {
unimplemented!()
}
fn update_project(_: HttpRequest) -> String {
unimplemented!()
}
fn delete_project(_: HttpRequest) -> String {
unimplemented!()
}
fn get_tasks(_: HttpRequest) -> String {
unimplemented!()
}
fn create_task(_: HttpRequest) -> String {
unimplemented!()
}
fn update_task(_: HttpRequest) -> String {
unimplemented!()
}
fn delete_task(_: HttpRequest) -> String {
unimplemented!()
}
fn main() {
// <scope>
App::new().scope("/project", |proj_scope| {
proj_scope
.resource("", |r| {
r.method(Method::GET).f(get_projects);
r.method(Method::POST).f(create_project)
})
.resource("/{project_id}", |r| {
r.method(Method::PUT).with(update_project);
r.method(Method::DELETE).f(delete_project)
})
.nested("/{project_id}/task", |task_scope| {
task_scope
.resource("", |r| {
r.method(Method::GET).f(get_tasks);
r.method(Method::POST).f(create_task)
})
.resource("/{task_id}", |r| {
r.method(Method::PUT).with(update_task);
r.method(Method::DELETE).with(delete_task)
})
})
});
// </scope>
}

View File

@ -0,0 +1,16 @@
// <ext>
use actix_web::{App, Error, HttpRequest, HttpResponse};
fn index(mut req: HttpRequest) -> Result<HttpResponse, Error> {
let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
Ok(HttpResponse::Ok().into())
}
fn main() {
let app = App::new()
.resource("/index.html", |r| r.f(index))
.external_resource("youtube", "https://youtube.com/watch/{video_id}")
.finish();
}
// </ext>

View File

@ -0,0 +1,20 @@
// <url>
use actix_web::{http::header, http::Method, App, HttpRequest, HttpResponse, Result};
fn index(req: HttpRequest) -> Result<HttpResponse> {
let url = req.url_for("foo", &["1", "2", "3"])?; // <- generate url for "foo" resource
Ok(HttpResponse::Found()
.header(header::LOCATION, url.as_str())
.finish())
}
fn main() {
let app = App::new()
.resource("/test/{a}/{b}/{c}", |r| {
r.name("foo"); // <- set resource name, then it could be used in `url_for`
r.method(Method::GET).f(|_| HttpResponse::Ok());
})
.route("/test/", Method::GET, index)
.finish();
}
// </url>