目录结构

Cargo.toml
[package]
name = "c01_hello_world"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-files.workspace = "0.6"
actix-session = { workspace = "0.9", features = ["cookie-session"] }
actix-web = "4.4"
async-stream = "0.3"
env_logger = "0.11"
log = "0.4"
main.rs
use std::{convert::Infallible, io};
use actix_files::{Files, NamedFile};
use actix_session::{storage::CookieSessionStore, Session, SessionMiddleware};
use actix_web::{
error, get,
http::{
header::{self, ContentType},
Method, StatusCode,
},
middleware, web, App, Either, HttpRequest, HttpResponse, HttpServer, Responder, Result,
};
use async_stream::stream;
static SESSION_SIGNING_KEY: &[u8] = &[0; 64];
#[get("/favicon")]
async fn favicon() -> Result<impl Responder> {
Ok(NamedFile::open("static/favicon.ico")?)
}
#[get("/welcome")]
async fn welcome(req: HttpRequest, session: Session) -> Result<HttpResponse> {
println!("{req:?}");
let mut counter = 1;
if let Some(count) = session.get::<i32>("counter")? {
println!("SESSION value: {count}");
counter = count + 1;
}
session.insert("counter", counter)?;
Ok(HttpResponse::build(StatusCode::OK)
.content_type(ContentType::plaintext())
.body(include_str!("../static/welcome.html")))
}
async fn default_handler(req_method: Method) -> Result<impl Responder> {
match req_method {
Method::GET => {
let file = NamedFile::open("static/404.html")?
.customize()
.with_status(StatusCode::NOT_FOUND);
Ok(Either::Left(file))
}
_ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
}
}
async fn response_body(path: web::Path<String>) -> HttpResponse {
let name = path.into_inner();
HttpResponse::Ok()
.content_type(ContentType::plaintext())
.streaming(stream! {
yield Ok::<_, Infallible>(web::Bytes::from("Hello "));
yield Ok::<_, Infallible>(web::Bytes::from(name));
yield Ok::<_, Infallible>(web::Bytes::from("!"));
})
}
async fn with_param(req: HttpRequest, path: web::Path<(String,)>) -> HttpResponse {
println!("{req:?}");
HttpResponse::Ok()
.content_type(ContentType::plaintext())
.body(format!("Hello {}!", path.0))
}
#[actix_web::main]
async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let key = actix_web::cookie::Key::from(SESSION_SIGNING_KEY);
log::info!("starting HTTP server at http://localhost:8080");
HttpServer::new(move || {
App::new()
.wrap(middleware::Compress::default())
.wrap(
SessionMiddleware::builder(CookieSessionStore::default(), key.clone())
.cookie_secure(false)
.build(),
)
.wrap(middleware::Logger::default())
.service(favicon)
.service(welcome)
.service(web::resource("/user/{name}").route(web::get().to(with_param)))
.service(web::resource("/async-body/{name}").route(web::get().to(response_body)))
.service(
web::resource("/test").to(|req: HttpRequest| match *req.method() {
Method::GET => HttpResponse::Ok(),
Method::POST => HttpResponse::MethodNotAllowed(),
_ => HttpResponse::NotFound(),
}),
)
.service(web::resource("/error").to(|| async {
error::InternalError::new(
io::Error::new(io::ErrorKind::Other, "test"),
StatusCode::INTERNAL_SERVER_ERROR,
)
}))
.service(Files::new("/static", "static").show_files_listing())
.service(
web::resource("/").route(web::get().to(|req: HttpRequest| async move {
println!("{req:?}");
HttpResponse::Found()
.insert_header((header::LOCATION, "static/welcome.html"))
.finish()
})),
)
.default_service(web::to(default_handler))
})
.bind(("127.0.0.1", 8080))?
.workers(2)
.run()
.await
}
404.html
<!DOCTYPE html>
<html>
<head>
<title>actix - basics</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon">
</head>
<body>
<a href="/static/welcome.html">back to home</a>
<h1>404</h1>
</body>
</html>
welcom.html
<!DOCTYPE html>
<html>
<head>
<title>actix - basics</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon">
</head>
<body>
<h1>Welcome <img width="30" height="30" src="/static/actixLogo.png"></h1>
</body>
</html>
访问:http://localhost:8080/static/actixLogo.png

访问:http://localhost:8080/

访问:http://localhost:8080/user/zhangdapeng

访问:http://localhost:8080/async-body/zhangdapeng

访问:http://localhost:8080/async-body111/zhangdapeng

核心代码1:如何响应静态文件
use std::{convert::Infallible, io};
use actix_files::{Files, NamedFile};
use actix_session::{storage::CookieSessionStore, Session, SessionMiddleware};
use actix_web::{
error, get,
http::{
header::{self, ContentType},
Method, StatusCode,
},
middleware, web, App, Either, HttpRequest, HttpResponse, HttpServer, Responder, Result,
};
#[get("/favicon")]
async fn favicon() -> Result<impl Responder> {
Ok(NamedFile::open("static/favicon.ico")?)
}
核心代码2:如何操作session和响应HTML
/// 设置首页的访问方式
#[get("/welcome")]
async fn welcome(req: HttpRequest, session: Session) -> Result<HttpResponse> {
println!("{req:?}");
// 记录session
let mut counter = 1;
// 从session中获取值,如果存在,则+1
if let Some(count) = session.get::<i32>("counter")? {
println!("SESSION value: {count}");
counter = count + 1;
}
// 设置session的值
session.insert("counter", counter)?;
// 返回响应
Ok(HttpResponse::build(StatusCode::OK)
.content_type(ContentType::plaintext())
.body(include_str!("../static/welcome.html")))
}
核心代码3:如何处理404错误
async fn default_handler(req_method: Method) -> Result<impl Responder> {
match req_method {
Method::GET => {
let file = NamedFile::open("static/404.html")?
.customize()
.with_status(StatusCode::NOT_FOUND);
Ok(Either::Left(file))
}
_ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
}
}
核心代码4:如何实现异步响应流
async fn default_handler(req_method: Method) -> Result<impl Responder> {
match req_method {
Method::GET => {
let file = NamedFile::open("static/404.html")?
.customize()
.with_status(StatusCode::NOT_FOUND);
Ok(Either::Left(file))
}
_ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
}
}
核心代码5:如何处理路径参数
async fn with_param(req: HttpRequest, path: web::Path<(String,)>) -> HttpResponse {
println!("{req:?}");
HttpResponse::Ok()
.content_type(ContentType::plaintext())
.body(format!("Hello {}!", path.0))
}