URL dispatch 提供了一种使用简单的模式匹配语言将URL映射到handler(处理程序)代码的简单方法。如果其中一种模式和与请求相关联的路径信息匹配,则调用特定的处理程序对象。
请求处理程序是一个函数,该函数接受零个或多个可以从请求中提取的参数(即impl FromRequest),并返回可以转换为HttpResponse
的类型(即impl Responder)。
有关更多信息,请查看handler section。
资源配置是向应用程序添加新资源的动作。 resource具有名称,该名称用作要用于URL生成的标识符。 该名称还允许开发人员将路由添加到现有resource。 resource还具有一种模式,旨在与URL的PATH部分(scheme和端口之后的部分,例如URL http://localhost:8080/foo/bar?q=value)中的*/foo/bar*)相匹配。 。 它与*QUERY部分(跟在?*之后的部分不匹配,例如*http://localhost:8080/foo/bar?q=value*中的*q=value*)。
App::route()方法提供了注册路由的简单方法。
此方法将单个路由添加到应用程序路由表。
此方法接受path pattern(路径模式),http方法和handler函数。
可以为同一路径多次调用route()
方法,在这种情况下,多个路由会注册同一resource路径。
use actix_web::{web, App, HttpResponse, HttpServer};
async fn index() -> HttpResponse {
HttpResponse::Ok().body("Hello")
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.route("/user", web::post().to(index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
虽然*App::route()*提供了注册路由的简单方法,但要访问完整的resource配置,则必须使用其他方法。 App::service()方法将单个resource添加到应用程序路由表。 此方法接受*path pattern*,guards,以及一个或多个routes。
use actix_web::{guard, web, App, HttpResponse};
fn index() -> HttpResponse {
HttpResponse::Ok().body("Hello")
}
pub fn main() {
App::new()
.service(web::resource("/prefix").to(index))
.service(
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())),
);
}
如果资源不包含任何route或不具有任何匹配的route,则它将返回NOT FOUND http响应。
Resource包含一组routes。
每个route依次具有一组guards
和一个handler。
可以使用Resource::route()
方法创建新route,该方法返回对新Route实例的引用。
默认情况下,route不包含任何guards,因此匹配所有请求,默认handler为HttpNotFound
。
该应用程序routes根据在resource注册和route注册期间定义的route标准传入requests。
Resource按照通过Resource::route()
注册routes的顺序匹配它包含的所有路由。
一个*Route*可以包含任意数量的*guards*,但只能包含一个handler。
App::new().service(
web::resource("/path").route(
web::route()
.guard(guard::Get())
.guard(guard::Header("content-type", "text/plain"))
.to(|| HttpResponse::Ok()),
),
)
在此示例中,如果请求包含Content-Type
header,此header的值为 *text/plain*且path等于/path
,则为GET请求返回HttpResponse::Ok()
。
如果resource无法匹配任何路由,则返回”NOT FOUND”响应。
ResourceHandler::route()返回Route对象。可以使用类似构建器的模式来配置路由。提供以下配置方法:
路由配置的主要目的是使请求的route与URL路径模式匹配(或不匹配)。 path
代表所请求URL的路径部分。
*actix-web*做到这一点的方法非常简单。
当请求进入系统时,对于系统中存在的每个resource配置声明,actix都会根据声明的模式检查请求的路径。
该检查按照通过App::service()
方法声明routes的顺序进行。
如果找不到resource,则将默认resource用作匹配的resource。
声明路由配置时,它可能包含路由guard参数。 与路由声明关联的所有路由guard必须为true,才能在检查期间将路由配置用于给定请求。 如果在检查过程中提供给路由配置的一组路由guard参数中的任何guard返回false,则该路由将被跳过,并且路由匹配将继续通过有序路由集合。
如果有任何路由匹配,则路由匹配过程停止,并调用与该路由关联的handler(处理程序)。如果在所有路由模式都用尽之后没有路由匹配,则返回*NOT FOUND*响应。
actix在pattern参数中使用的模式匹配语言的语法很简单。 路由配置中使用的模式可以以斜杠字符开头。
如果该模式不以斜杠字符开头,则在匹配时将在其前面添加一个隐式斜杠。 例如,以下模式是等效的:
{foo}/bar/baz
和:
/{foo}/bar/baz
以{identifier}的形式指定可变部分(替换标记),其含义是“接受直到下一个斜杠字符的任何字符,并将其用作HttpRequest.match_info()
对象中的名称”。
模式中的替换标记与正则表达式[^{}/]+
相匹配。
match_info是 Params
对象,代表基于路由模式从URL提取的动态部分。
它可以作为request.match_info
使用。
例如,以下模式定义一个文字段(foo)和两个替换标记(baz和bar):
foo/{baz}/{bar}
上面的模式将匹配这些URL,生成以下匹配信息:
foo/1/2 -> Params {'baz':'1', 'bar':'2'}
foo/abc/def -> Params {'baz':'abc', 'bar':'def'}
但是,它将不符合以下模式:
foo/1/2/ -> No match (trailing slash)
bar/abc/def -> First segment literal mismatch
匹配一个segment(段)中segment替换标记将仅进行到模式中的segment中第一个非字母数字字符为止。因此,例如,如果使用此路由模式:
foo/{name}.html
文字路径*/foo/biz.html将与上述路由模式匹配,并且匹配结果将为Params{'name': 'biz'}
。
但是,文字路径/foo/biz将不匹配,因为它在由{name}.html表示的段末尾不包含文字.html*(它仅包含biz,而不包含biz.html)。
要捕获两个片段,可以使用两个替换标记:
foo/{name}.{ext}
literal path(文字路径)*/foo/biz.html*将与上述路由模式匹配,匹配结果将为*Params{‘name’: ‘biz’, ‘ext’: ‘html’}。发生这种情况是因为.字面部分。 (期间)两个替换标记{name}和{ext}*之间。
Replacement markers(替换标记)可以选择指定正则表达式,该正则表达式将用于确定路径段是否应与标记匹配。 若要指定替换标记仅应匹配正则表达式定义的一组特定字符,则必须使用稍微扩展形式的替换标记语法。 在大括号内,替换标记名称必须后跟冒号,然后是正则表达式。 与替换标记*[^/]+关联的默认正则表达式匹配一个或多个非斜杠字符。 例如,在内部,替换标记{foo}可以更详细地拼写为{foo:[^/]+}。 您可以将其更改为任意正则表达式以匹配任意字符序列,例如{foo:\d+}*以仅匹配数字。
Segments(段)必须至少包含一个字符才能匹配segment替换标记。例如,对于URL */abc/*:
注意:在匹配模式之前,将对路径进行URL-unquoted并将其解码为有效的Unicode字符串,并且表示匹配路径段的值也将被URL-unquoted。
因此,例如,以下模式:
foo/{bar}
匹配以下URL时:
http://example.com/foo/La%20Pe%C3%B1a
matchdict看起来像这样(值是URL-decoded):
Params{'bar': 'La Pe\xf1a'}
路径段中的文字字符串应代表提供给actix的路径的decoded(解码)值。您不希望在模式中使用URL-encoded(URL编码)的值。例如,而不是这样:
/Foo%20Bar/{baz}
您将要使用以下内容:
/Foo Bar/{baz}
有可能获得”tail match”。为此,必须使用自定义正则表达式。
foo/{bar}/{tail:.*}
上面的模式将匹配这些URL,生成以下匹配信息:
foo/1/2/ -> Params{'bar':'1', 'tail': '2/'}
foo/abc/def/a/b/c -> Params{'bar':u'abc', 'tail': 'def/a/b/c'}
范围界定可以帮助您组织共享公共根路径的路由。您可以将范围嵌套在范围内。
假设您要组织用于查看”Users”的端点的路径。这些路径可能包括:
这些路径的scoped布局如下所示
async fn show_users() -> HttpResponse {
HttpResponse::Ok().body("Show users")
}
async fn user_detail(path: web::Path<(u32,)>) -> HttpResponse {
HttpResponse::Ok().body(format!("User detail: {}", path.0))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
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")?
.run()
.await
}
*scoped*路径可以包含可变路径段作为resources。与unscoped的路径一致。
您可以从HttpRequest::match_info()
获得可变路径段。Path
extractor还能够提取范围级别的变量段。
HttpRequest::match_info
中提供了表示匹配路径段的所有值。可以使用Path::get()
检索特定值。
use actix_web::{HttpRequest, HttpResponse, Result};
async fn index(req: HttpRequest) -> Result<String> {
let v1: u8 = req.match_info().get("v1").unwrap().parse().unwrap();
let v2: u8 = req.match_info().query("v2").parse().unwrap();
let (v3, v4): (u8, u8) = req.match_info().load().unwrap();
Ok(format!("Values {} {} {} {}", v1, v2, v3, v4))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.route("/a/{v1}/{v2}/", web::get().to(index))
.route("", web::get().to(|| HttpResponse::Ok()))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
对于路径’/a/1/2/‘的此示例,值v1和v2将解析为“1”和“2”。
可以从尾部路径参数创建PathBuf
。
返回的PathBuf
是百分比解码的。
如果segment等于“..”,则跳过前一个segment(如果有)。
为了安全起见,如果segment满足以下任一条件,则返回Err表示满足条件:
.
(除了 ..
), *
:
, >
, <
/
这些条件的结果是,从请求路径参数解析的PathBuf
可以安全地插在路径中或用作路径的后缀,而无需其他检查。
use actix_web::{HttpRequest, Result};
use std::path::PathBuf;
async fn index(req: HttpRequest) -> Result<String> {
let path: PathBuf = req.match_info().query("tail").parse().unwrap();
Ok(format!("Path {:?}", path))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| App::new().route(r"/a/{tail:.*}", web::get().to(index)))
.bind("127.0.0.1:8088")?
.run()
.await
}
Actix提供了用于类型安全路径信息提取的功能。
Path提取信息,destination类型可以用几种不同的形式定义。
最简单的方法是使用元组类型。
元组中的每个元素必须对应于路径模式中的一个元素。
也就是说,您可以将路径模式/{id}/{username}/
与Path<(u32, String)>
类型进行匹配,但是Path<(String, String, String)>
类型将始终失败。
use actix_web::{web, Result};
async fn index(info: web::Path<(String, u32)>) -> Result<String> {
Ok(format!("Welcome {}! id: {}", info.0, info.1))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/{username}/{id}/index.html", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
也可以将路径模式信息提取到struct(结构)。在这种情况下,此结构必须实现*serde的*Deserialize特性。
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
// extract path info using serde
async fn index(info: web::Path<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/{username}/index.html", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
Query为请求查询参数提供了类似的功能。
使用HttpRequest.url_for()方法可基于resource模式生成URL。例如,如果您使用名称“foo”和模式”{a}/{b}/{c}“配置了资源,则可以执行以下操作:
use actix_web::{guard, http::header, HttpRequest, HttpResponse, Result};
async 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())
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.service(
web::resource("/test/{a}/{b}/{c}")
.name("foo") // <- set resource name, then it could be used in `url_for`
.guard(guard::Get())
.to(|| HttpResponse::Ok()),
)
.route("/test/", web::get().to(index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
这将返回类似字符串*http://example.com/test/1/2/3*的信息(至少在当前协议和主机名暗含http://example.com的情况下)。
url_for()
方法返回Url object,因此您可以修改此url(添加查询参数,锚点等)。
只能为named resources调用url_for()
,否则将返回错误。
可以将有效URLs的Resources注册为外部Resources。它们仅用于生成URL,而在请求时从不考虑匹配。
use actix_web::{HttpRequest, Responder};
async fn index(req: HttpRequest) -> impl Responder {
let url = req.url_for("youtube", &["oHg5SJYRHA0"]).unwrap();
assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
url.into_string()
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.external_resource("youtube", "https://youtube.com/watch/{video_id}")
.route("/", actix_web::web::get().to(index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
通过规范化意味着:
handler找到正确解析的路径后立即返回。 如果全部启用,则规范化条件的顺序为1)合并,2)合并和附加以及3)附加。 如果路径至少满足这些条件之一,则它将重定向到新路径。
use actix_web::{middleware, HttpResponse};
async fn index() -> HttpResponse {
HttpResponse::Ok().body("Hello")
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new()
.wrap(middleware::NormalizePath)
.route("/resource/", web::to(index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
在此示例中,//resource///
将重定向到/resource/
。
在此示例中,所有方法都注册了路径规范化处理程序,但您不应依赖此机制来重定向POST请求。 带有斜杠的*Not Found*的重定向会将POST请求转换为GET,从而丢失原始请求中的所有POST数据。 可以仅针对GET请求注册路径规范化:
use actix_web::{http::Method, middleware, web, App, HttpServer};
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(middleware::NormalizePath)
.route("/resource/", web::get().to(index))
.default_service(web::route().method(Method::GET))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
web::scope()
方法允许设置特定的应用程序scope(范围)。
此scope表示resource前缀,该resource将附加到resource配置添加的所有resource模式中。
这有助于路由配置,同时仍保持相同的resource名称。
例如:
async fn show_users() -> HttpResponse {
HttpResponse::Ok().body("Show users")
}
async fn user_detail(path: web::Path<(u32,)>) -> HttpResponse {
HttpResponse::Ok().body(format!("User detail: {}", path.0))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
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")?
.run()
.await
}
在上面的示例中,*show_users路由的有效路由模式为/users/show而不是/show,因为应用程序的scope将位于该模式之前。
然后,仅当URL路径为/users/show*时,路由才会匹配,并且使用路由名称show_users调用HttpRequest.url_for()
函数时,它将生成具有相同路径的URL。
您可以将guard视为可以接受*request*对象引用并返回true 或 *false*的简单函数。
形式上,guard是实现Guard
特征的任何对象。
Actix提供了多个谓词,您可以检查api文档的functions section。
这是一个简单的guard,用于检查请求是否包含特定的*header*:
use actix_web::{dev::RequestHead, guard::Guard, http, HttpResponse};
struct ContentTypeHeader;
impl Guard for ContentTypeHeader {
fn check(&self, req: &RequestHead) -> bool {
req.headers().contains_key(http::header::CONTENT_TYPE)
}
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{web, App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/",
web::route()
.guard(ContentTypeHeader)
.to(|| HttpResponse::Ok()),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
在此示例中,仅当请求包含CONTENT-TYPE header时才调用index 处理程序。 Guards无法访问或修改请求对象,但是可以在request extensions中存储其他信息。
您可以通过将任何谓词值包装在Not
谓词中来反转其含义。例如,如果要为除“GET”以外的所有方法返回“METHOD NOT ALLOWED”响应:
use actix_web::{guard, web, App, HttpResponse, HttpServer};
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().route(
"/",
web::route()
.guard(guard::Not(guard::Get()))
.to(|| HttpResponse::MethodNotAllowed()),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
Any
guard均会接受guards列表,如果提供的guards中的任何一个相匹配,则他们将匹配。即:
guard::Any(guard::Get()).or(guard::Post())
All
guard接受guard列表,如果所有提供的guard都匹配,则匹配。即:
guard::All(guard::Get()).and(guard::Header("content-type", "plain/text"))
如果在路由表中找不到路径模式,或者resource找不到匹配的路由,则使用默认resource。
默认响应为*NOT FOUND*。
可以使用App::default_service()
覆盖*NOT FOUND*响应。
此方法接受与App::service()
方法的常规resource配置相同的配置功能。
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(web::resource("/").route(web::get().to(index)))
.default_service(
web::route()
.guard(guard::Not(guard::Get()))
.to(|| HttpResponse::MethodNotAllowed()),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}