The reason I chose actix-web
over rocket
is 1) it doesn't rely on the nightly version 2) it support web socket
1, Hello world with actix-web
1) Create a empty project, cargo new actixtest
2) Edit Cargo.toml
to add actix-web
, at time of writing this article, the latest version is 1.0.9
[dependencies]
actix-web = "1.0.9"
3) Edit main.rs
to code the server:
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
fn index() -> impl Responder {
HttpResponse::Ok().body("hello world!")
}
fn main() {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
})
.bind("127.0.0.1:7000")
.unwrap()
.run()
.unwrap();
}
The code is quite straightforward: create new app and setup routes (route url to handler), and put app inside a http server instance.
4) Build and run: cargo run
5) Now visit http://127.0.0.1:7000/, you can see "hello world!"
2, RESTful URL
When we want to provide RESTful API, we might align several methods to the same resource url, for example, align method get and put to "/users". Here we use web::resource
to rewrite the routing part code:
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
fn index() -> impl Responder {
HttpResponse::Ok().body("hello world!")
}
fn get_users() -> impl Responder {
HttpResponse::Ok().body("[Alice, Bob]")
}
fn put_users() -> impl Responder {
// here do some logic to put a new user
HttpResponse::Ok().body("success")
}
fn main() {
HttpServer::new(|| {
App::new()
.service(
web::resource("/")
.route(web::get().to(index))
)
.service(
web::resource("/users")
.route(web::get().to(get_users))
.route(web::put().to(put_users))
)
})
.bind("127.0.0.1:7000")
.unwrap()
.run()
.unwrap();
}
3, Pass parameter from URL
Now we want to support when user visit "/hello/{name}", let the page print "hello {name}", for example, "/hello/alice" will print out "hello alice"
First let's import another class HttpRequest
from the actix_web package, so change the first line to:
use actix_web::{web, App, HttpServer, HttpRequest, HttpResponse, Responder};
Now let's add the URL routing part:
...
.service(
web::resource("/hello/{name}")
.route(web::get().to(say_hello))
)
...
Last let's add the say_hello
handler function:
fn say_hello(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap();
let resp = format!("hello {}", name);
HttpResponse::Ok().body(resp)
}
We get the name
value from req's match info, then format the return string and return the response.
Now visit http://127.0.0.1:7000/hello/alice, you will see "hello alice", visit http://127.0.0.1:7000/hello/bob, you will see "hello bob"
4, Render page from template file
To render html out, we are going to use a template engine called tera
, so first let's add tera
to Cargo.toml
[dependencies]
actix-web = "1.0.9"
tera = "0.11"
Now let's create our template files. Let's first create a folder called templates
, put it at the same level with file Cargo.toml
, so like this:
actixtest
|- Cargo.toml
|- templates
|- src
Inside templates folder, create 2 files: base.html
and index.html
:
base.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Actix Web</title>
</head>
<body>
{% block content %}
{% endblock content %}
</body>
</html>
Then in index.html
, we will inherit the base.html
and overwrite the content
block:
{% extends "base.html" %}
{% block content %}
<h1>hello {{name}}</h1>
{% endblock content %}
Ok, now our template files are ready, let's setup tera in our main.rs.
First import it:
use tera::{Tera, Context};
Then in HttpServer::new
closure function, before the line App::new()
, let's setup the tera instance:
let tera =
Tera::new(
concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")
).unwrap();
You can see here we pass the "templates" folder path to Tera, Tera will find all files under that folder and compile them.
Now let's create a data struct to save all the data for the App Context
struct AppData {
tmpl: Tera
}
Around the line App::new()
, add:
App::new()
.data(AppData {tmpl: tera})
Ok, now we have the tera instance and passed this instance to app data in the app context. Let's setup a new route url to test template rendering:
.service(
web::resource("/tmpl/{name}")
.route(web::get().to(render_tmpl))
)
Last we define this render_tmpl
handler:
fn render_tmpl(data: web::Data<AppData>, req:HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap();
let mut ctx = Context::new();
ctx.insert("name", name);
let rendered = data.tmpl.render("index.html", &ctx).unwrap();
HttpResponse::Ok().body(rendered)
}
Now cargo run
, visit http://127.0.0.1:7000/tmpl/bob you will see the html version "hello bob" inside a h1
tag from our index.html
template.
Final code
Here is our final complete code:
use actix_web::{web, App, HttpServer, HttpRequest, HttpResponse, Responder};
use tera::{Tera, Context};
fn index() -> impl Responder {
HttpResponse::Ok().body("hello world!")
}
fn get_users() -> impl Responder {
HttpResponse::Ok().body("[Alice, Bob]")
}
fn put_users() -> impl Responder {
// here do some logic to put a new user
HttpResponse::Ok().body("success")
}
fn say_hello(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap();
let resp = format!("hello {}", name);
HttpResponse::Ok().body(resp)
}
fn render_tmpl(data: web::Data<AppData>, req:HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap();
let mut ctx = Context::new();
ctx.insert("name", name);
let rendered = data.tmpl.render("index.html", &ctx).unwrap();
HttpResponse::Ok().body(rendered)
}
struct AppData {
tmpl: Tera
}
fn main() {
HttpServer::new(|| {
let tera =
Tera::new(
concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")
).unwrap();
App::new()
.data(AppData {tmpl: tera})
.service(
web::resource("/")
.route(web::get().to(index))
)
.service(
web::resource("/users")
.route(web::get().to(get_users))
.route(web::put().to(put_users))
)
.service(
web::resource("/hello/{name}")
.route(web::get().to(say_hello))
)
.service(
web::resource("/tmpl/{name}")
.route(web::get().to(render_tmpl))
)
})
.bind("127.0.0.1:7000")
.unwrap()
.run()
.unwrap();
}
Top comments (0)