HTTP Request URL Parameters Syntax
| Pattern |
Kind |
Description |
:name |
Normal |
Matches a path piece, excludes / |
:name? |
Optional |
Matches an optional path piece, excludes / |
/:name?/ /:name? |
OptionalSegment |
Matches an optional path segment, excludes /, prefix or suffix should be / |
+ :name+ |
OneOrMore |
Matches a path piece, includes / |
* :name* |
ZeroOrMore |
Matches an optional path piece, includes / |
/*/ /* /:name*/ /:name* |
ZeroOrMoreSegment |
Matches zero or more path segments, prefix or suffix should be / |
⚡️ Quick Start
use hypers's full feature
use hypers::{prelude::*, Handler};
use std::time::Instant;
use utoipa::{IntoParams, OpenApi, ToSchema};
#[derive(Serialize, Deserialize, ToSchema, Extractor)]
pub struct HeaderParams {
pub host: Option<String>,
#[serde(rename(deserialize = "user-agent"))]
pub user_agent: Option<String>,
pub accept: Option<String>,
}
#[derive(Serialize, Deserialize, IntoParams, ToSchema, Debug, Extractor)]
#[into_params(parameter_in = Query)]
#[hypers(extract(source(from = "path"), source(from = "query"), source(from = "body"),))]
pub struct Data {
pub id: u16,
pub name: String,
pub age: u16,
}
#[get("/parse_cookies")]
pub async fn parse_cookies(
hypers: CookieParam<String, true>,
rust: CookieParam<String, true>,
) -> impl Responder {
Ok::<_, Error>((
200,
format!("first cookie is {}, second cookie is {}", hypers, rust),
))
}
#[utoipa::path(
get,
path = "/user/parse_header",
tag = "parse request headers",
responses(
(status = 200, description = "Parse header from request successfully", body = HeaderParams),
(status = 404, description = "Parse header from request failed")
)
)]
#[get("/parse_header")]
pub async fn parse_header(req: &mut Request) -> impl Responder {
let header_params = req.parse_header::<HeaderParams>()?;
Ok::<_, Error>((200, header_params))
}
#[utoipa::path(
delete,
path = "/user/parse_param/{id}/{name}/{age}",
tag = "parse request url path params",
params(
("id" = u16, Path, description = "Id of readme to delete"),
("age" = u16, Path, description = "Age of readme to delete"),
("name" = String, Path, description = "Name of readme to delete"),
),
responses(
(status = 200, description = "Parse Url Path Params successfully",body = Data),
(status = 404, description = "Parse Url Path Params failed")
),
security(
("api_key" = [])
)
)]
#[delete("parse_param/:id/:name/:age")]
pub async fn parse_param(
req: &mut Request,
id: Path<u16>,
name: Path<String>,
age: Path<u16>,
) -> impl Responder {
let app_state_user = req.get::<Data>("user");
println!("app_state_user = {:?}", app_state_user);
(
200,
Data {
id: id.0,
name: name.0,
age: age.0,
},
)
}
#[utoipa::path(
get,
path = "/user/parse_query",
tag = "parse request url query params",
params(
("id" = u16, Query, description = "Id of readme to get"),
("age" = u16, Query, description = "Age of readme to get"),
("name" = String, Query, description = "Name of readme to get"),
),
responses(
(status = 200, description = "Parse query successfully", body = QueryParams)
)
)]
#[get("parse_query")]
pub async fn parse_query(
id: Query<u16, true>,
name: Query<String, true>,
age: Query<u16, true>,
) -> impl Responder {
Ok::<_, Error>((
200,
Data {
id: id.inner(),
name: name.inner(),
age: age.inner(),
},
))
}
#[utoipa::path(
patch,
path = "/user/parse_form_patch",
tag = "parse request body",
request_body(
content = Data,
content_type = "application/x-www-form-urlencoded",
),
responses(
(status = 200, description = "FormParams created successfully", body = Data),
(status = 409, description = "FormParams already exists")
)
)]
#[patch("parse_form_patch")]
pub async fn parse_form_patch(user: Data) -> impl Responder {
Ok::<_, Error>((200, user))
}
#[utoipa::path(
post,
path = "/user/parse_json",
tag = "parse request body",
request_body(
content = Data,
content_type = "application/json",
),
responses(
(status = 201, description = "Data item created successfully", body = Data),
(status = 409, description = "Data already exists")
)
)]
#[post("parse_json")]
pub async fn parse_json(user: Json<Data>) -> impl Responder {
Ok::<_, Error>((200, user))
}
#[post("multipart_form")]
pub async fn multipart_form(form_data: Data) -> impl Responder {
Ok::<_, Error>((200, form_data))
}
#[post("multipart_file")]
pub async fn multipart_file(req: &mut Request) -> impl Responder {
let file = req.file("file").await?;
let file_name = file.name()?;
let file_name = file_name.to_string();
let img = req.files("imgs").await?;
let imgs_name = img
.iter()
.map(|m| m.name().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
Some((
200,
format!("file_name = {}, imgs_name = {}", file_name, imgs_name),
))
}
#[utoipa::path(
get,
path = "/base/query",
tag = "url query params",
params(
("name" = Vec<String>, Query, description = "Url Query Params name"),
("age" = u16, Query, description = "Url Query Params age"),
),
responses(
(status = 200, description = "successfully, response = String"),
(status = 404, description = "failed")
)
)]
#[get("/query")]
pub async fn query(req: &mut Request) -> impl Responder {
let name = req.query::<Vec<String>>("name")?;
let age = req.query::<u16>("age")?;
let key = req.get::<&str>("key")?;
println!("app_state = {}", key);
Some((200, format!("name = {:?} , age = {}", name, age)))
}
#[get("/set_cookies")]
pub async fn set_cookies() -> impl Responder {
let mut cookie1 = Cookie::new("hypers", "hypers2023");
cookie1.set_path("/user");
let mut cookie2 = Cookie::new("rust", "rust2023");
cookie2.set_path("/user");
let mut cookie_jar = CookieJar::new();
cookie_jar.add(cookie1);
cookie_jar.add(cookie2);
(200, cookie_jar)
}
#[get("/html")]
pub async fn html() -> impl Responder {
Text::Html("<html><body>hello</body></html>")
}
#[get(":name/ws")]
pub async fn websocket(req: &mut Request) -> impl Responder {
let name = req.param::<String>("name").unwrap_or_default();
WebSocketUpgrade::new()
.upgrade(req, |mut ws| async move {
while let Ok(msg) = ws.receive().await {
if let Some(msg) = msg {
match msg {
Message::Text(text) => {
let text = format!("{},{}", name, text);
ws.send(Message::Text(text)).await.unwrap();
}
Message::Close(_) => break,
_ => {}
}
}
}
})
.await
}
#[handler]
pub async fn stat_time(req: &mut Request, next: &Next) -> Result {
let start_time = Instant::now();
let res = next.handle(req, next).await?;
let elapsed_time = start_time.elapsed();
println!(
"The current request processing function takes time :{:?}",
elapsed_time
);
Ok(res)
}
#[handler]
pub async fn app_state(req: &mut Request, next: &Next) -> Result {
let user = Data {
id: 1,
name: "admin".to_owned(),
age: 21,
};
req.set("user", user);
req.set("key", "Hello World");
next.handle(req, next).await
}
#[derive(OpenApi)]
#[openapi(
info(title = "Base Api", description = "Base Api description"),
paths(query),
tags(
(name = "base", description = " base router")
)
)]
struct ApiDoc1;
#[derive(OpenApi)]
#[openapi(
info(title = "User Api", description = "User Api description"),
paths(parse_header, parse_param, parse_query, parse_form_patch, parse_json),
components(schemas(HeaderParams,Data,)),
tags(
(name = "user", description = " user router ")
)
)]
struct ApiDoc2;
fn main() -> Result<()> {
let mut root = Router::new("");
root.get("/*", StaticDir::new("src").listing(true));
root.handler(websocket);
let mut base = Router::new("base");
base.hook(app_state, vec!["/"], None);
base.handlers(handlers!(query, set_cookies, html))
.openapi(ApiDoc1::openapi());
let mut user = Router::new("user");
user.hook(stat_time, vec!["/user"], None);
user.handlers(handlers!(
parse_cookies,
parse_header,
parse_param,
parse_query,
parse_form_patch,
parse_json,
multipart_form,
multipart_file
))
.openapi(ApiDoc2::openapi());
root.push(base);
root.push(user);
root.swagger("swagger-ui");
println!("root router = {:#?}", root);
#[cfg(not(any(feature = "rustls", feature = "native_tls")))]
{
hypers::run(root, "127.0.0.1:7878")
}
#[cfg(feature = "rustls")]
{
let tls = RustlsConfig::new()
.cert(include_bytes!("./certs/cert.pem").to_vec())
.key(include_bytes!("./certs/key.pem").to_vec());
hypers::run(root, "127.0.0.1:7878", tls)
}
#[cfg(feature = "native_tls")]
{
let tls = NativeTls::from_pkcs12(include_bytes!("./certs/identity.p12"), "mypass")?;
hypers::run(root, "127.0.0.1:7878", tls)
}
}