rust既然是系統(tǒng)級的編程語言,所以當(dāng)然也能用來開發(fā) web,不過想我這樣凡夫俗子,肯定不能從頭自己寫一個 web 服務(wù)器,肯定要依賴已經(jīng)存在的 rust web開發(fā)框架來完成 web 開發(fā)。
rust目前比較有名的框架是iron和nickel,我們兩個都寫一下簡單的使用教程。
接上一篇,使用cargo獲取第三方庫。cargo new mysite --bin
在cargo.toml中添加iron的依賴,
[dependencies]
iron = "*"
然后build將依賴下載到本地 cargo build
如果報(bào)ssl錯誤,那可能你需要安裝linux的ssl開發(fā)庫。
首先還是從 hello world 開始吧,繼續(xù)抄襲官方的例子:
extern crate iron;
use iron::prelude::*;
use iron::status;
fn main() {
Iron::new(|_: &mut Request| {
Ok(Response::with((status::Ok, "Hello World!")))
}).http("localhost:3000").unwrap();
}
然后運(yùn)行
cargo run
使用curl直接就可以訪問你的網(wǎng)站了。
curl localhost:3000
Hello World!
仔細(xì)一看,發(fā)現(xiàn)這個例子很無厘頭啊,對于習(xí)慣了寫python的我來說,確實(shí)不習(xí)慣。 簡單點(diǎn)看:
iron::new().http("localhost:3000").unwrap()
這句是服務(wù)器的基本的定義,new內(nèi)部是一個rust lambda 表達(dá)式
let plus_one = |x: i32| x + 1;
assert_eq!(2, plus_one(1));
具體的怎么使用 ,可以暫時不用理會,因?yàn)槟阒灰廊绾瓮瓿蓋eb,因?yàn)槲乙膊粫??!?結(jié)合之前一章節(jié)的json處理,我們來看看web接口怎么返回json,當(dāng)然也要 rustc_serialize 放到 cargo.toml 中
下面的代碼直接參考開源代碼地址
extern crate iron;
extern crate rustc_serialize;
use iron::prelude::*;
use iron::status;
use rustc_serialize::json;
#[derive(RustcEncodable)]
struct Greeting {
msg: String
}
fn main() {
fn hello_world(_: &mut Request) -> IronResult<Response> {
let greeting = Greeting { msg: "Hello, World".to_string() };
let payload = json::encode(&greeting).unwrap();
Ok(Response::with((status::Ok, payload)))
}
Iron::new(hello_world).http("localhost:3000").unwrap();
println!("On 3000");
}
執(zhí)行 cargo run 使用 curl 測試結(jié)果:
curl localhost:3000
{"msg":"Hello, World"}
當(dāng)然可以可以實(shí)現(xiàn)更多的業(yè)務(wù)需求,通過控制自己的json。
既然有了json了,如果要多個路由什么的,豈不是完蛋了,所以不可能這樣的,我們需要考慮一下怎么實(shí)現(xiàn)路由的定制
不說話直接上代碼,同一樣要在你的cargo.toml文件中添加對router的依賴
extern crate iron;
extern crate router;
extern crate rustc_serialize;
use iron::prelude::*;
use iron::status;
use router::Router;
use rustc_serialize::json;
#[derive(RustcEncodable, RustcDecodable)]
struct Greeting {
msg: String
}
fn main() {
let mut router = Router::new();
router.get("/", hello_world);
router.post("/set", set_greeting);
fn hello_world(_: &mut Request) -> IronResult<Response> {
let greeting = Greeting { msg: "Hello, World".to_string() };
let payload = json::encode(&greeting).unwrap();
Ok(Response::with((status::Ok, payload)))
}
// Receive a message by POST and play it back.
fn set_greeting(request: &mut Request) -> IronResult<Response> {
let payload = request.body.read_to_string();
let request: Greeting = json::decode(payload).unwrap();
let greeting = Greeting { msg: request.msg };
let payload = json::encode(&greeting).unwrap();
Ok(Response::with((status::Ok, payload)))
}
Iron::new(router).http("localhost:3000").unwrap();
}
這次添加了路由的實(shí)現(xiàn)和獲取客戶端發(fā)送過來的數(shù)據(jù),有了get,post,所以現(xiàn)在一個基本的api網(wǎng)站已經(jīng)完成了。不過 并不是所有的網(wǎng)站都是api來訪問,同樣需要html模版引擎和直接返回靜態(tài)頁面。等等
vagrant@ubuntu-14:~/tmp/test/rustprimer/mysite$ cargo build
Compiling mysite v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/mysite)
src/main.rs:29:36: 29:52 error: no method named `read_to_string` found for type `iron::request::Body<'_, '_>` in the current scope
src/main.rs:29 let payload = request.body.read_to_string();
^~~~~~~~~~~~~~~~
src/main.rs:29:36: 29:52 help: items from traits can only be used if the trait is in scope; the following trait is implemented but not in scope, perhaps add a `use` for it:
src/main.rs:29:36: 29:52 help: candidate #1: use `std::io::Read`
error: aborting due to previous error
Could not compile `mysite`.
編譯出錯了,太糟糕了,提示說沒有read_to_string這個方法,然后我去文檔查了一下,發(fā)現(xiàn)有read_to_string方法 再看提示信息
src/main.rs:29:36: 29:52 help: items from traits can only be used if the trait is in scope; the following trait is implemented but not in scope, perhaps add a `use` for it:
src/main.rs:29:36: 29:52 help: candidate #1: use `std::io::Read`
讓我們添加一個std::io::Read
,這個如果操作過文件,你一定知道怎么寫,添加一下,應(yīng)該能過去了,還是繼續(xù)出錯了,看看報(bào)錯
Compiling mysite v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/mysite)
src/main.rs:30:36: 30:52 error: this function takes 1 parameter but 0 parameters were supplied [E0061]
src/main.rs:30 let payload = request.body.read_to_string();
^~~~~~~~~~~~~~~~
src/main.rs:30:36: 30:52 help: run `rustc --explain E0061` to see a detailed explanation
src/main.rs:31:46: 31:53 error: mismatched types:
expected `&str`,
found `core::result::Result<usize, std::io::error::Error>`
(expected &-ptr,
found enum `core::result::Result`) [E0308]
src/main.rs:31 let request: Greeting = json::decode(payload).unwrap();
^~~~~~~
src/main.rs:31:46: 31:53 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:30:36: 30:52 error: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements [E0495]
src/main.rs:30 let payload = request.body.read_to_string();
^~~~~~~~~~~~~~~~
src/main.rs:29:5: 35:6 help: consider using an explicit lifetime parameter as shown: fn set_greeting<'a>(request: &mut Request<'a, 'a>) -> IronResult<Response>
src/main.rs:29 fn set_greeting(request: &mut Request) -> IronResult<Response> {
src/main.rs:30 let payload = request.body.read_to_string();
src/main.rs:31 let request: Greeting = json::decode(payload).unwrap();
src/main.rs:32 let greeting = Greeting { msg: request.msg };
src/main.rs:33 let payload = json::encode(&greeting).unwrap();
src/main.rs:34 Ok(Response::with((status::Ok, payload)))
...
error: aborting due to 3 previous errors
Could not compile `mysite`.
第一句提示我們,這個read_to_string(),至少要有一個參數(shù),但是我們一個都沒有提供。 我們看看read_to_string的用法
se std::io;
use std::io::prelude::*;
use std::fs::File;
let mut f = try!(File::open("foo.txt"));
let mut buffer = String::new();
try!(f.read_to_string(&mut buffer));
用法比較簡單,我們修改一下剛剛的函數(shù):
fn set_greeting(request: &mut Request) -> IronResult<Response> {
let mut payload = String::new();
request.body.read_to_string(&mut payload);
let request: Greeting = json::decode(&payload).unwrap();
let greeting = Greeting { msg: request.msg };
let payload = json::encode(&greeting).unwrap();
Ok(Response::with((status::Ok, payload)))
}
從request中讀取字符串,讀取的結(jié)果存放到payload中,然后就可以進(jìn)行操作了,編譯之后運(yùn)行,使用curl提交一個post數(shù)據(jù)
$curl -X POST -d '{"msg":"Just trust the Rust"}' http://localhost:3000/set
{"msg":"Just trust the Rust"}
iron 基本告一段落 當(dāng)然還有如何使用html模版引擎,那就是直接看文檔就行了。
當(dāng)然既然是web框架肯定是iron能干的nicke也能干,所以那我們就看看如何做一個hello 和返回一個html 的頁面
同樣我們創(chuàng)建cargo new site --bin
,然后添加nickel到cargo.toml中,cargo build
#[macro_use] extern crate nickel;
use nickel::Nickel;
fn main() {
let mut server = Nickel::new();
server.utilize(router! {
get "**" => |_req, _res| {
"Hello world!"
}
});
server.listen("127.0.0.1:6767");
}
簡單來看,也就是這樣回事。
router!
宏,傳入的參數(shù)是 get 方法和對應(yīng)的路徑,"**"是全路徑匹配。當(dāng)然我們要引入關(guān)于html模版相關(guān)的信息
#[macro_use] extern crate nickel;
use std::collections::HashMap;
use nickel::{Nickel, HttpRouter};
fn main() {
let mut server = Nickel::new();
server.get("/", middleware! { |_, response|
let mut data = HashMap::new();
data.insert("name", "user");
return response.render("site/assets/template.tpl", &data);
});
server.listen("127.0.0.1:6767");
}
上面的信息你可以編譯,使用curl看看發(fā)現(xiàn)出現(xiàn)
$ curl http://127.0.0.1:6767
Internal Server Error
看看文檔,沒發(fā)現(xiàn)什么問題,我緊緊更換了一個文件夾的名字,這個文件夾我也創(chuàng)建了。 然后我在想難道是服務(wù)器將目錄寫死了嗎?于是將上面的路徑改正這個,問題解決。
return response.render("examples/assets/template.tpl", &data);
我們看一下目錄結(jié)構(gòu)
.
|-- Cargo.lock
|-- Cargo.toml
|-- examples
| `-- assets
| `-- template.tpl
|-- src
| `-- main.rs