1
0
mirror of https://github.com/actix/actix-website synced 2025-01-23 00:25:55 +01:00

URL-Dispatch is done-ish.

This commit is contained in:
Cameron Dershem 2019-06-25 18:20:36 -04:00
parent b9c78c1a58
commit be68e418b0
18 changed files with 264 additions and 164 deletions

View File

@ -10,9 +10,10 @@ URL dispatch provides a simple way for mapping URLs to handler code using a simp
matching language. If one of the patterns matches the path information associated with a request, matching language. If one of the patterns matches the path information associated with a request,
a particular handler object is invoked. a particular handler object is invoked.
> A handler is a specific object that implements the `Handler` trait, defined in your > A request handler is a function that accepts zero or more parameters that can be extracted
> application, that receives the request and returns a response object. More information > from a request (ie, [*impl FromRequest*][implfromrequest]) and returns a type that can
> is available in the [handler section][sec4handler]. > be converted into an HttpResponse (ie, [*impl Responder*][implresponder]). More information
> is available in the [handler section][handlersection].
# Resource configuration # Resource configuration
@ -32,40 +33,33 @@ for the same path, in that case, multiple routes register for the same resource
{{< include-example example="url-dispatch" section="main" >}} {{< include-example example="url-dispatch" section="main" >}}
While *App::route()* provides simple way of registering routes, to access complete resource While *App::route()* provides simple way of registering routes, to access complete resource
configuration, a different method has to be used. The [*App::resource()*][appresource] method configuration, a different method has to be used. The [*App::service()*][appservice] method
adds a single resource to application routing table. This method accepts a *path pattern* adds a single [resource][webresource] to application routing table. This method accepts a
and a resource configuration function. *path pattern*, guards, and one or more routes.
{{< include-example example="url-dispatch" file="resource.rs" section="resource" >}} {{< include-example example="url-dispatch" file="resource.rs" section="resource" >}}
The *Configuration function* has the following type:
```rust
FnOnce(&mut Resource<_>) -> ()
```
The *Configuration function* can set a name and register specific routes.
If a resource does not contain any route or does not have any matching routes, it If a resource does not contain any route or does not have any matching routes, it
returns *NOT FOUND* http response. returns *NOT FOUND* http response.
## Configuring a Route ## Configuring a Route
Resource contains a set of routes. Each route in turn has a set of predicates and a handler. Resource contains a set of routes. Each route in turn has a set of `guards` and a handler.
New routes can be created with `Resource::route()` method which returns a reference New routes can be created with `Resource::route()` method which returns a reference
to new *Route* instance. By default the *route* does not contain any predicates, so matches to new *Route* instance. By default the *route* does not contain any guards, so matches
all requests and the default handler is `HttpNotFound`. all requests and the default handler is `HttpNotFound`.
The application routes incoming requests based on route criteria which are defined during The application routes incoming requests based on route criteria which are defined during
resource registration and route registration. Resource matches all routes it contains in resource registration and route registration. Resource matches all routes it contains in
the order the routes were registered via `Resource::route()`. 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 *guards* but only one handler.
{{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}} {{< include-example example="url-dispatch" file="cfg.rs" section="cfg" >}}
In this example, `HttpResponse::Ok()` is returned for *GET* requests. In this example, `HttpResponse::Ok()` is returned for *GET* requests if the request
If a request contains `Content-Type` header, the value of this header is *text/plain*, contains `Content-Type` header, the value of this header is *text/plain*, and path
and path equals to `/path`, Resource calls handle of the first matching route. equals to `/path`.
If a resource can not match any route, a "NOT FOUND" response is returned. If a resource can not match any route, a "NOT FOUND" response is returned.
@ -77,19 +71,16 @@ can be configured with a builder-like pattern. Following configuration methods a
* [*Route::method()*][routemethod] registers a method guard. Any number of guards can be * [*Route::method()*][routemethod] registers a method guard. Any number of guards can be
registered for each route. registered for each route.
* [*Route::to()*][routeto] registers handler function for this route. Only one handler * [*Route::to()*][routeto] registers handler function for this route. Only one handler
can be registered. Usually handler registration is the last config operation. Handler can be registered. Usually handler registration is the last config operation.
function can be a function or closure and has the type `Fn(HttpRequest<S>) -> R + 'static`
* [*Route::to_async()*][routetoasync] registers an async handler function for this route. * [*Route::to_async()*][routetoasync] registers an async handler function for this route.
Only one handler can be registered. Handler registration is the last config operation. Only one handler can be registered. Handler registration is the last config operation.
Handler function can be a function or closure and has the type
`Fn(HttpRequest<S>) -> Future<Item = HttpResponse, Error = Error> + 'static`
# Route matching # Route matching
The main purpose of route configuration is to match (or not match) the request's `path` The main purpose of route configuration is to match (or not match) the request's `path`
against a URL path pattern. `path` represents the path portion of the URL that was requested. against a URL path pattern. `path` represents the path portion of the URL that was requested.
The way that *actix* does this is very simple. When a request enters the system, The way that *actix-web* does this is very simple. When a request enters the system,
for each resource configuration declaration present in the system, actix checks for each resource configuration declaration present in the system, actix checks
the request's path against the pattern declared. This checking happens in the order that the request's path against the pattern declared. This checking happens in the order that
the routes were declared via `App::resource()` method. If resource can not be found, the routes were declared via `App::resource()` method. If resource can not be found,
@ -261,12 +252,12 @@ A *scoped* path can contain variable path segments as resources. Consistent with
unscoped paths. unscoped paths.
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][pathextractor] also is able to extract scope level variable segments.
# Match information # Match information
All values representing matched path segments are available in [`HttpRequest::match_info`][matchinfo]. All values representing matched path segments are available in [`HttpRequest::match_info`][matchinfo].
Specific values can be retrieved with [`Params::get()`][paramsget]. Specific values can be retrieved with [`Path::get()`][pathget].
{{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}} {{< include-example example="url-dispatch" file="minfo.rs" section="minfo" >}}
@ -339,16 +330,9 @@ normalization conditions, if all are enabled, is 1) merge, 2) both merge and app
3) append. If the path resolves with at least one of those conditions, it will redirect 3) append. If the path resolves with at least one of those conditions, it will redirect
to the new path. to the new path.
If *append* is *true*, append slash when needed. If a resource is defined with trailing
slash and the request doesn't have one, it will be appended automatically.
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*.
{{< include-example example="url-dispatch" file="norm.rs" section="norm" >}} {{< include-example example="url-dispatch" file="norm.rs" section="norm" >}}
In this example `/resource`, `//resource///` will be redirected to `/resource/`. In this example `//resource///` will be redirected to `/resource/`.
In this example, the path normalization handler is registered for all methods, In this example, the path normalization handler is registered for all methods,
but you should not rely on this mechanism to redirect *POST* requests. The redirect of the but you should not rely on this mechanism to redirect *POST* requests. The redirect of the
@ -385,12 +369,12 @@ and returns *true* or *false*. Formally, a guard is any object that implements t
Here is a simple guard that check that a request contains a specific *header*: Here is a simple guard that check that a request contains a specific *header*:
{{< include-example example="url-dispatch" file="pred.rs" section="pred" >}} {{< include-example example="url-dispatch" file="guard.rs" section="guard" >}}
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.
Guards have access to the application's state via `HttpRequest::data()`. Also predicates Guards can not access or modify the request object, but it is possible to store extra
can store extra information in [request extensions][httprequest]. information in [request extensions][requestextensions].
## Modifying guard values ## Modifying guard values
@ -398,7 +382,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":
{{< include-example example="url-dispatch" file="pred2.rs" section="pred" >}} {{< include-example example="url-dispatch" file="guard2.rs" section="guard2" >}}
The `Any` guard accepts a list of guards and matches if any of the supplied The `Any` guard accepts a list of guards and matches if any of the supplied
guards match. i.e: guards match. i.e:
@ -424,21 +408,25 @@ with `App::service()` method.
{{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}} {{< include-example example="url-dispatch" file="dhandler.rs" section="default" >}}
[sec4handler]: sec-4-handler.html [handlersection]: ../handlers/
[approute]: ../../actix-web/actix_web/struct.App.html#method.route [approute]: https://docs.rs/actix-web/1.0.2/actix_web/struct.App.html#method.route
[appresource]: ../../actix-web/actix_web/struct.App.html#method.resource [appservice]: https://docs.rs/actix-web/1.0.2/actix_web/struct.App.html?search=#method.service
[resourcehandler]: ../../actix-web/actix_web/dev/struct.ResourceHandler.html#method.route [webresource]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Resource.html
[route]: ../../actix-web/actix_web/dev/struct.Route.html [resourcehandler]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Resource.html#method.route
[routeguard]: ../../actix-web/actix_web/dev/struct.Route.html#method.guard [route]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html
[routemethod]: ../../actix-web/actix_web/dev/struct.Route.html#method.method [routeguard]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.guard
[routeto]: ../../actix-web/actix_web/dev/struct.Route.html#method.to [routemethod]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.method
[routetoasync]: ../../actix-web/actix_web/dev/struct.Route.html#method.to_async [routeto]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.to
[matchinfo]: ../actix_web/struct.HttpRequest.html#method.match_info [routetoasync]: https://docs.rs/actix-web/1.0.2/actix_web/struct.Route.html#method.to_async
[paramsget]: ../actix_web/dev/struct.Params.html#method.get [matchinfo]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.match_info
[pathstruct]: ../../actix-web/actix_web/struct.Path.html [pathget]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html#method.get
[query]: ../../actix-web/actix_web/struct.Query.html [pathstruct]: https://docs.rs/actix-web/1.0.2/actix_web/dev/struct.Path.html
[urlfor]: ../../actix-web/actix_web/struct.HttpRequest.html#method.url_for [query]: https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Query.html
[urlobj]: https://docs.rs/url/1.7.0/url/struct.Url.html [urlfor]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.url_for
[guardtrait]: ../actix_web/guard/trait.Guard.html [urlobj]: https://docs.rs/url/1.7.2/url/struct.Url.html
[guardfuncs]: ../../actix-web/actix_web/guard/index.html#functions [guardtrait]: https://docs.rs/actix-web/1.0.2/actix_web/guard/trait.Guard.html
[httprequest]: ../../actix-web/actix_web/struct.HttpRequest.html#method.extensions [guardfuncs]: https://docs.rs/actix-web/1.0.2/actix_web/guard/index.html#functions
[requestextensions]: https://docs.rs/actix-web/1.0.2/actix_web/struct.HttpRequest.html#method.extensions
[implfromrequest]: https://docs.rs/actix-web/1.0.2/actix_web/trait.FromRequest.html
[implresponder]: https://docs.rs/actix-web/1.0.2/actix_web/trait.Responder.html
[pathextractor]: ../extractors

View File

@ -1,7 +1,11 @@
// <cfg>
use actix_web::{guard, web, App, HttpResponse}; use actix_web::{guard, web, App, HttpResponse};
#[rustfmt::skip]
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
// <cfg>
App::new().service( App::new().service(
web::resource("/path").route( web::resource("/path").route(
web::route() web::route()
@ -9,6 +13,11 @@ pub fn main() {
.guard(guard::Header("content-type", "text/plain")) .guard(guard::Header("content-type", "text/plain"))
.to(|| HttpResponse::Ok()), .to(|| HttpResponse::Ok()),
), ),
); )
}
// </cfg> // </cfg>
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}

View File

@ -1,4 +1,4 @@
use actix_web::{guard, web, App, HttpRequest, HttpResponse, Responder}; use actix_web::{guard, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
fn index(_req: HttpRequest) -> impl Responder { fn index(_req: HttpRequest) -> impl Responder {
"Welcome!" "Welcome!"
@ -6,12 +6,18 @@ fn index(_req: HttpRequest) -> impl Responder {
// <default> // <default>
pub fn main() { pub fn main() {
HttpServer::new(|| {
App::new() App::new()
.service(web::resource("/").route(web::get().to(index))) .service(web::resource("/").route(web::get().to(index)))
.default_service( .default_service(
web::route() web::route()
.guard(guard::Not(guard::Get())) .guard(guard::Not(guard::Get()))
.to(|| HttpResponse::MethodNotAllowed()), .to(|| HttpResponse::MethodNotAllowed()),
); )
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </default> // </default>

View File

@ -0,0 +1,28 @@
// <guard>
use actix_web::{
dev::RequestHead, guard::Guard, http, web, App, HttpResponse, HttpServer,
};
struct ContentTypeHeader;
impl Guard for ContentTypeHeader {
fn check(&self, req: &RequestHead) -> bool {
req.headers().contains_key(http::header::CONTENT_TYPE)
}
}
pub fn main() {
HttpServer::new(|| {
App::new().route(
"/",
web::route()
.guard(ContentTypeHeader)
.to(|| HttpResponse::Ok()),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </guard>

View File

@ -0,0 +1,18 @@
// <guard2>
use actix_web::{guard, web, App, HttpResponse, HttpServer};
pub fn main() {
HttpServer::new(|| {
App::new().route(
"/",
web::route()
.guard(guard::Not(guard::Get()))
.to(|| HttpResponse::MethodNotAllowed()),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
}
// </guard2>

View File

@ -1,28 +1,34 @@
pub mod cfg; pub mod cfg;
pub mod dhandler; pub mod dhandler;
pub mod guard;
pub mod guard2;
pub mod minfo; pub mod minfo;
pub mod norm; pub mod norm;
pub mod norm2; pub mod norm2;
pub mod path; pub mod path;
pub mod path2; pub mod path2;
pub mod pbuf; pub mod pbuf;
pub mod pred;
pub mod pred2;
pub mod resource; pub mod resource;
pub mod scope; pub mod scope;
pub mod url_ext; pub mod url_ext;
pub mod urls; pub mod urls;
// <main> // <main>
use actix_web::{web, App, HttpRequest, HttpResponse}; use actix_web::{web, App, HttpResponse, HttpServer};
fn index(_req: HttpRequest) -> HttpResponse { fn index() -> HttpResponse {
unimplemented!() HttpResponse::Ok().body("Hello")
} }
fn main() { fn main() {
HttpServer::new(|| {
App::new() App::new()
.route("/user/{name}", web::get().to(index)) .route("/", web::get().to(index))
.route("/user/{name}", web::post().to(index)); .route("/user", web::post().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </main> // </main>

View File

@ -9,8 +9,16 @@ fn index(req: HttpRequest) -> Result<String> {
} }
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new() App::new()
.route("/a/{v1}/{v2}/", web::get().to(index)) .route("/a/{v1}/{v2}/", web::get().to(index))
.route("", web::get().to(|| actix_web::HttpResponse::Ok())); .route("", web::get().to(|| actix_web::HttpResponse::Ok()))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </minfo> // </minfo>

View File

@ -1,14 +1,21 @@
// <norm> // <norm>
use actix_web::{middleware, web, App}; use actix_web::{middleware, web, App, HttpResponse};
fn index() -> HttpResponse {
HttpResponse::Ok().body("Hello")
}
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new() App::new()
.wrap(middleware::NormalizePath) .wrap(middleware::NormalizePath)
.route("/", web::get().to(index)); .route("/resource/", web::to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </norm> // </norm>
use actix_web::HttpRequest;
fn index(_req: HttpRequest) -> String {
unimplemented!()
}

View File

@ -1,16 +1,22 @@
// <norm> // <norm>
use actix_web::{http::Method, middleware, web, App}; use actix_web::{http::Method, middleware, web, App, HttpServer};
pub fn main() { pub fn main() {
HttpServer::new(|| {
App::new() App::new()
.wrap(middleware::NormalizePath) .wrap(middleware::NormalizePath)
.route("/resource/", web::get().to(index)) .route("/resource/", web::get().to(index))
.default_service(web::route().method(Method::GET)); .default_service(web::route().method(Method::GET))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </norm> // </norm>
use actix_web::HttpRequest; use actix_web::HttpResponse;
fn index(_req: HttpRequest) -> String { fn index() -> HttpResponse {
unimplemented!() HttpResponse::Ok().body("Hello")
} }

View File

@ -1,15 +1,22 @@
// <path> // <path>
use actix_web::{web, App, Result}; use actix_web::{web, App, Result};
// extract path info using serde
fn index(info: web::Path<(String, u32)>) -> Result<String> { fn index(info: web::Path<(String, u32)>) -> Result<String> {
Ok(format!("Welcome {}! id: {}", info.0, info.1)) Ok(format!("Welcome {}! id: {}", info.0, info.1))
} }
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new().route( App::new().route(
"/{username}/{id}/index.html", // <- define path parameters "/{username}/{id}/index.html", // <- define path parameters
web::get().to(index), web::get().to(index),
); )
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </path> // </path>

View File

@ -13,9 +13,17 @@ fn index(info: web::Path<Info>) -> Result<String> {
} }
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new().route( App::new().route(
"/{username}/index.html", // <- define path parameters "/{username}/index.html", // <- define path parameters
web::get().to(index), web::get().to(index),
); )
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </path> // </path>

View File

@ -8,6 +8,12 @@ fn index(req: HttpRequest) -> Result<String> {
} }
pub fn main() { pub fn main() {
App::new().route(r"/a/{tail:.*}", web::get().to(index)); use actix_web::HttpServer;
HttpServer::new(|| App::new().route(r"/a/{tail:.*}", web::get().to(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </pbuf> // </pbuf>

View File

@ -1,20 +0,0 @@
// <pred>
use actix_web::{dev::RequestHead, guard::Guard, http, web, App, HttpResponse};
struct ContentTypeHeader;
impl Guard for ContentTypeHeader {
fn check(&self, req: &RequestHead) -> bool {
req.headers().contains_key(http::header::CONTENT_TYPE)
}
}
pub fn main() {
App::new().route(
"",
web::route()
.guard(ContentTypeHeader)
.to(|| HttpResponse::Ok()),
);
}
// </pred>

View File

@ -1,12 +0,0 @@
// <pred>
use actix_web::{guard, web, App, HttpResponse};
pub fn main() {
App::new().route(
"/",
web::route()
.guard(guard::Not(guard::Get()))
.to(|| HttpResponse::MethodNotAllowed()),
);
}
// </pred>

View File

@ -1,15 +1,19 @@
// <resource> // <resource>
use actix_web::{web, App, HttpRequest, HttpResponse}; use actix_web::{guard, web, App, HttpResponse};
fn index(_req: HttpRequest) -> HttpResponse { fn index() -> HttpResponse {
unimplemented!() HttpResponse::Ok().body("Hello")
} }
pub fn main() { pub fn main() {
App::new() App::new()
.service(web::resource("/prefix").to(index)) .service(web::resource("/prefix").to(index))
.service( .service(
web::resource("/user/{name}").route(web::get().to(|| HttpResponse::Ok())), web::resource("/user/{name}")
.name("user_detail")
.guard(guard::Header("content-type", "application/json"))
.route(web::get().to(|| HttpResponse::Ok()))
.route(web::put().to(|| HttpResponse::Ok())),
); );
} }
// </resource> // </resource>

View File

@ -1,11 +1,25 @@
use actix_web::{web, App, HttpRequest, HttpResponse}; use actix_web::{web, App, HttpResponse, HttpServer};
// <scope> // <scope>
fn show_users(_req: HttpRequest) -> HttpResponse { fn show_users() -> HttpResponse {
unimplemented!() HttpResponse::Ok().body("Show users")
}
fn user_detail(_path: web::Path<(u32,)>) -> HttpResponse {
HttpResponse::Ok().body("User detail")
} }
pub fn main() { pub fn main() {
App::new().service(web::scope("/users").route("/show", web::get().to(show_users))); HttpServer::new(|| {
App::new().service(
web::scope("/users")
.route("/show", web::get().to(show_users))
.route("/show/{id}", web::get().to(user_detail)),
)
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </scope> // </scope>

View File

@ -9,9 +9,17 @@ fn index(req: HttpRequest) -> impl Responder {
} }
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new() App::new()
.route("/index.html", web::get().to(index)) .route("/", web::get().to(index))
.external_resource("youtube", "https://youtube.com/watch/{video_id}") .external_resource("youtube", "https://youtube.com/watch/{video_id}")
.route("/", actix_web::web::get().to(index)); .route("/", actix_web::web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </ext> // </ext>

View File

@ -3,12 +3,16 @@ use actix_web::{guard, http::header, web, App, HttpRequest, HttpResponse, Result
fn index(req: HttpRequest) -> Result<HttpResponse> { fn index(req: HttpRequest) -> Result<HttpResponse> {
let url = req.url_for("foo", &["1", "2", "3"])?; // <- generate url for "foo" resource let url = req.url_for("foo", &["1", "2", "3"])?; // <- generate url for "foo" resource
Ok(HttpResponse::Found() Ok(HttpResponse::Found()
.header(header::LOCATION, url.as_str()) .header(header::LOCATION, url.as_str())
.finish()) .finish())
} }
pub fn main() { pub fn main() {
use actix_web::HttpServer;
HttpServer::new(|| {
App::new() App::new()
.service( .service(
web::resource("/test/{a}/{b}/{c}") web::resource("/test/{a}/{b}/{c}")
@ -16,6 +20,11 @@ pub fn main() {
.guard(guard::Get()) .guard(guard::Get())
.to(|| HttpResponse::Ok()), .to(|| HttpResponse::Ok()),
) )
.route("/test/", web::get().to(index)); .route("/test/", web::get().to(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.unwrap();
} }
// </url> // </url>