{"id":15893850,"url":"https://github.com/chinanf-boy/httptest-zh","last_synced_at":"2026-04-21T10:04:06.200Z","repository":{"id":90548410,"uuid":"162805527","full_name":"chinanf-boy/httptest-zh","owner":"chinanf-boy","description":"中文翻译:\u003cbrson/httptest\u003e 让我们用 Rust 搞个 web service 和 client ，用Iron和Hyper :heart: 校对 ✅","archived":false,"fork":false,"pushed_at":"2019-03-29T04:49:39.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-10T15:19:24.501Z","etag":null,"topics":["http","hyper","iron","rust"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chinanf-boy.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-22T11:26:08.000Z","updated_at":"2019-03-29T04:49:40.000Z","dependencies_parsed_at":"2023-07-19T01:15:54.890Z","dependency_job_id":null,"html_url":"https://github.com/chinanf-boy/httptest-zh","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chinanf-boy/httptest-zh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhttptest-zh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhttptest-zh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhttptest-zh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhttptest-zh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chinanf-boy","download_url":"https://codeload.github.com/chinanf-boy/httptest-zh/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhttptest-zh/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272236503,"owners_count":24897442,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-26T02:00:07.904Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","hyper","iron","rust"],"created_at":"2024-10-06T08:13:54.917Z","updated_at":"2026-04-21T10:04:06.157Z","avatar_url":"https://github.com/chinanf-boy.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# brson/httptest [![explain]][source] [![translate-svg]][translate-list]\n\n\u003c!-- [![size-img]][size] --\u003e\n\n[explain]: http://llever.com/explain.svg\n[source]: https://github.com/chinanf-boy/Source-Explain\n[translate-svg]: http://llever.com/translate.svg\n[translate-list]: https://github.com/chinanf-boy/chinese-translate-list\n[size-img]: https://packagephobia.now.sh/badge?p=Name\n[size]: https://packagephobia.now.sh/result?p=Name\n\n「 让我们用 Rust 搞个 web service 和 client ，用[Iron]和[Hyper]  」\n\n[中文](./readme.md) | [english](https://github.com/brson/httptest)\n\n---\n\n## 校对 ✅\n\n\u003c!-- doc-templite START generated --\u003e\n\u003c!-- repo = 'brson/httptest' --\u003e\n\u003c!-- commit = '1d2b6c9b81bdd6cb3b67be3c9245389462e89426' --\u003e\n\u003c!-- time = '2017-07-04' --\u003e\n翻译的原文 | 与日期 | 最新更新 | 更多\n---|---|---|---\n[commit] | ⏰ 2017-07-04 | ![last] | [中文翻译][translate-list]\n\n[last]: https://img.shields.io/github/last-commit/brson/httptest.svg\n[commit]: https://github.com/brson/httptest/tree/1d2b6c9b81bdd6cb3b67be3c9245389462e89426\n\n\u003c!-- doc-templite END generated --\u003e\n\n### 贡献\n\n欢迎 👏 勘误/校对/更新贡献 😊 [具体贡献请看](https://github.com/chinanf-boy/chinese-translate-list#贡献)\n\n## 生活\n\n[If help, **buy** me coffee —— 营养跟不上了，给我来瓶营养快线吧! 💰](https://github.com/chinanf-boy/live-need-money)\n\n---\n\n### 目录\n\n\u003c!-- START doctoc --\u003e\n\u003c!-- END doctoc --\u003e\n\n# 让我们用 Rust 搞个 web service 和 client\n\n所以我正在研究这个项目[Crater]做的[Rust]回归测试。研究在 Rust 中编写部件的可行性。我需要一个能说 JSON 的 HTTP 服务器，以及相应的客户端。我没有看到很多关于如何做到这一点的文档，所以我在这里记录我的调查。\n\n我打算用[Iron]和[Hyper]，都没有用过.\n\n此仓库中的每个提交都对应一个章节，因此如果您愿意,请跟随.\n\n**请编辑: 当内部不准确! [Thanks /r/rust!](http://www.reddit.com/r/rust/comments/33k1yn/lets_make_a_web_service_and_client/)**\n\n# 1. 准备服务一些 JSON\n\n我首先要求 Cargo 给我一个名为'httptest'的新可执行项目。传递`--bin`，创建一个源文件`src/main.rs`，终会被编译到一个应用程序(二进制)。\n\n```text\n$ cargo new httptest --bin\n```\n\n我会用[Iron]作为服务器(框架),请按照对应文档中的说明，将以下内容添加到我的`Cargo.toml`文件.\n\n```toml\n[dependencies]\niron = \"*\"\n```\n\n然后进入`main.rs`，只是复制他们的例子.\n\n```rust\nextern crate iron;\n\nuse iron::prelude::*;\nuse iron::status;\n\nfn main() {\n    fn hello_world(_: \u0026mut Request) -\u003e IronResult\u003cResponse\u003e {\n        Ok(Response::with((status::Ok, \"Hello World!\")))\n    }\n\n    Iron::new(hello_world).http(\"localhost:3000\").unwrap();\n    println!(\"On 3000\");\n}\n```\n\n然后，输入`cargo build`.\n\n```text\n$ cargo build\nCompiling hyper v0.3.13\nCompiling iron v0.1.16\nCompiling httptest v0.2.0 (file:///opt/dev/httptest)\n```\n\n迄今为止，要取得了成功哦。喔呼，Rust 超爽快的。让我们尝试运行服务器`cargo run`.\n\n```text\n$ cargo run\nRunning `target/debug/httptest`\n```\n\n我坐在这里等待一段时间，期待它打印\"On 3000\"，但它永远不会打印。Cargo 要捕获输出，以此让我们看看我们是否正在服务。\n\n```text\n$ curl http://localhost:3000\nHello World!\n```\n\n哦,酷。我们现在知道如何构建 Web 服务器。良好的起点。\n\n# 2. 让我们把 结构 变为 JSON\n\n[rustc-serialize]仍然是转换为 JSON 和，从 JSON 转换的最简单方法? 也许我应该用[serde]，若你真的想要[serde_macros]的话，但这只适用于 Rust 每晚版本。我应该只使用每晚版本吗? 没有其他人需要使用它。\n\n让我们继续尝试真实的 rustc-serialize。现在我的 Cargo.toml 的'依赖关系(dependencies)'部分如下所示.\n\n```toml\n[dependencies]\niron = \"*\"\nrustc-serialize = \"*\"\n```\n\n基于[rustc-serialize 文档](https://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/index.html)，我更新了`main.rs`，看起来像这样:\n\n```rust\nextern crate iron;\nextern crate rustc_serialize;\n\nuse iron::prelude::*;\nuse iron::status;\nuse rustc_serialize::json;\n\n#[derive(RustcEncodable)]\nstruct Greeting {\n    msg: String\n}\n\nfn main() {\n    fn hello_world(_: \u0026mut Request) -\u003e IronResult\u003cResponse\u003e {\n        let greeting = Greeting { msg: \"Hello, World\".to_string() };\n        let payload = json::encode(\u0026greeting).unwrap();\n        Ok(Response::with((status::Ok, payload)))\n    }\n\n    Iron::new(hello_world).http(\"localhost:3000\").unwrap();\n    println!(\"On 3000\");\n}\n```\n\n然后，`cargo build`.(以下错误，源自我的失误，上面例子是对的)\n\n```text\n$ cargo build\n   Updating registry `https://github.com/rust-lang/crates.io-index`\n Downloading unicase v1.1.1\n \u003c... more downloading here ...\u003e\n   Compiling lazy_static v0.1.15\n \u003c... more compiling here ...\u003e\n   Compiling httptest v0.2.0 (file:///opt/dev/httptest)\nsrc/main.rs:17:12: 17:47 error: this function takes 1 parameter but 2 parameters were supplied [E0061]\nsrc/main.rs:17         Ok(Response::with(status::Ok, payload))\n                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nsrc/main.rs:17:12: 17:47 help: run `rustc --explain E0061` to see a detailed explanation\nerror: aborting due to previous error\nCould not compile `httptest`.\n\nTo learn more, run the command again with --verbose.\n```\n\n现在又多了很多新依赖，但，有一个[`Response::with`](http://ironframework.io/doc/iron/response/struct.Response.html#method.with)错误出来:\n\n```rust\nfn with\u003cM: Modifier\u003cResponse\u003e\u003e(m: M) -\u003e Response\n```\n\n我不知道`Modifier`是什么。[查查文档](http://ironframework.io/doc/iron/modifiers/index.html)也没有帮助。我不知道该怎么做，但我注意到原来的例子传递了一个元组给`Response::with`，而我新的`Response::with`处理，则有两个参数。似乎元组是一个`Modifier`.\n\n将元组添加到`Ok(Response::with((status::Ok, payload)))`, 再执行`cargo run`, curl 一些 JSON.\n\n```\n$ curl http://localhost:3000\n{\"msg\":\"Hello, World\"}\n```\n\nBoom! 我们正在发送 JSON。是时候加上另一些'佐料'了.\n\n# 3. 路由\n\n接下来我想 POST 一些 JSON，但在我这样做之前，我需要一个合适的 URL 来 POST，所以我想我需要学习如何设置路由。\n\n我看着 Iron 的[文档](http://ironframework.io/doc/iron/)，并且在正文中没有看到任何明显的东西，但是有一个叫做[router](http://ironframework.io/doc/router/index.html)的箱子，可能很有趣。\n\n它的模块文档，说明了\"`Router`为 Iron 提供了快速灵活的路由\"，但其他内容并不多。我如何使用`Router`?! 在我做了很多侦探工作之后，我发现了[一个示例](https://github.com/iron/router/blob/master/examples/simple.rs)好吧,让我们试着让它适应我们不断发展的实验。\n\n我加`router = \"*\"`到`Cargo.toml`的 `[dependencies]`部分，然后开始写作。以下是我在阅读 POST 数据之前，想到的。\n\n```rust\nextern crate iron;\nextern crate router;\nextern crate rustc_serialize;\n\nuse iron::prelude::*;\nuse iron::status;\nuse router::Router;\nuse rustc_serialize::json;\n\n#[derive(RustcEncodable, RustcDecodable)]\nstruct Greeting {\n    msg: String\n}\n\nfn main() {\n    let mut router = Router::new();\n\n    router.get(\"/\", hello_world);\n    router.post(\"/set\", set_greeting);\n\n    fn hello_world(_: \u0026mut Request) -\u003e IronResult\u003cResponse\u003e {\n        let greeting = Greeting { msg: \"Hello, World\".to_string() };\n        let payload = json::encode(\u0026greeting).unwrap();\n        Ok(Response::with((status::Ok, payload)))\n    }\n\n    // Receive a message by POST and play it back.\n    fn set_greeting(request: \u0026mut Request) -\u003e IronResult\u003cResponse\u003e {\n        let payload = request.body.read_to_string();\n        let request: Greeting = json::decode(payload).unwrap();\n        let greeting = Greeting { msg: request.msg };\n        let payload = json::encode(\u0026greeting).unwrap();\n        Ok(Response::with((status::Ok, payload)))\n    }\n\n    Iron::new(router).http(\"localhost:3000\").unwrap();\n}\n```\n\n这使用了`Router`来控制，处理程序调度。它构建后，仍会响应`curl http://localhost:3000`，但是`/set`路由并没有真正实现。\n\n现在将 POST body 读入字符串。这个[`Request`的文档](http://ironframework.io/doc/iron/request/struct.Request.html)说这`body`字段是一个迭代器，因此我们只需要将该迭代器收集到一个字符串中。\n\n我第一次尝试`let payload = request.body.read_to_string();`因为我知道它曾经有效过。\n\n但现在没有了。\n\n```text\n$ cargo build\nCompiling httptest v0.2.0 (file:///opt/dev/httptest)\nsrc/main.rs:29:36: 29:52 error: no method named `read_to_string` found for type `iron::request::Body\u003c'_, '_\u003e` in the current scope\nsrc/main.rs:29         let payload = request.body.read_to_string();\n                                                  ^~~~~~~~~~~~~~~~\nsrc/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:\nsrc/main.rs:29:36: 29:52 help: candidate #1: use `std::io::Read`\nerror: aborting due to previous error\nCould not compile `httptest`.\n\nTo learn more, run the command again with --verbose.\n```\n\n我厌恶地举起双手(屏蔽)。为什么这个方法不再存在?! Rust 团队总是捉弄我们! 但,我注意到编译器在一定程度上已经解释了方法的存在，并告诉我应该导入`std::io::Read`。\n\n我添加导入`read_to_string`并发现其行为与我想象的不同.\n\n```text\n101 $ cargo build\nCompiling httptest v0.2.0 (file:///opt/dev/httptest)\nsrc/main.rs:30:36: 30:52 error: this function takes 1 parameter but 0 parameters were supplied [E0061]\nsrc/main.rs:30         let payload = request.body.read_to_string();\n                                                  ^~~~~~~~~~~~~~~~\n```\n\n好吧，现在`Read::read_to_string`的函数签名是`fn read_to_string(\u0026mut self, buf: \u0026mut String) -\u003e Result\u003cusize, Error\u003e`，以此来提供缓冲区并处理错误。重写`set_greeting`方法.\n\n```rust\n    // 接收一个来自POST的信息，并使其返回\n    fn set_greeting(request: \u0026mut Request) -\u003e IronResult\u003cResponse\u003e {\n        let mut payload = String::new();\n        request.body.read_to_string(\u0026mut payload).unwrap();\n        let request: Greeting = json::decode(\u0026payload).unwrap();\n        let greeting = Greeting { msg: request.msg };\n        let payload = json::encode(\u0026greeting).unwrap();\n        Ok(Response::with((status::Ok, payload)))\n    }\n```\n\n让我们运行这个并给它一个卷曲.\n\n```text\n$ curl -X POST -d '{\"msg\":\"Just trust the Rust\"}' http://localhost:3000/set\n{\"msg\":\"Just trust the Rust\"}\n```\n\n哦，Rust，终于好了。你太烦了.\n\n# 4. 变一变\n\n是的，我知道所有`.unwrap()`们很糟糕。但我不在乎。我们正在做原型.\n\n在我们继续编写客户端之前，我想修改这个玩具示例，可在`/set` POST 上存储一些状态，以便以后再使用。我搞出一个本地`greeting`，在一些闭包中捕获它，然后看看编译器有什么抱怨的。\n\n这是我的新`main`函数,在尝试编译之前:\n\n```rust\nfn main() {\n    let mut greeting = Greeting { msg: \"Hello, World\".to_string() };\n\n    let mut router = Router::new();\n\n    router.get(\"/\", |r| hello_world(r, \u0026greeting));\n    router.post(\"/set\", |r| set_greeting(r, \u0026mut greeting));\n\n    fn hello_world(_: \u0026mut Request, greeting: \u0026Greeting) -\u003e IronResult\u003cResponse\u003e {\n        let payload = json::encode(\u0026greeting).unwrap();\n        Ok(Response::with((status::Ok, payload)))\n    }\n\n    // 接收一个来自POST的信息，并使其返回\n    fn set_greeting(request: \u0026mut Request, greeting: \u0026mut Greeting) -\u003e IronResult\u003cResponse\u003e {\n        let mut payload = String::new();\n        request.body.read_to_string(\u0026mut payload).unwrap();\n        *greeting = json::decode(\u0026payload).unwrap();\n        Ok(Response::with(status::Ok))\n    }\n\n    Iron::new(router).http(\"localhost:3000\").unwrap();\n}\n```\n\n```text\nsrc/main.rs:21:12: 21:51 error: type mismatch resolving `for\u003c'r, 'r, 'r\u003e \u003c[closure@src/main.rs:21:21: 21:50 greeting:_] as core::ops::FnOnce\u003c(\u0026'r mut iron::request::Request\u003c'r, 'r\u003e,)\u003e\u003e::Output == core::result::Result\u003ciron::response::Response, iron::error::IronError\u003e`:\n expected bound lifetime parameter ,\n    found concrete lifetime [E0271]\nsrc/main.rs:21     router.get(\"/\", |r| hello_world(r, \u0026greeting));\n                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nsrc/main.rs:21:12: 21:51 help: run `rustc --explain E0271` to see a detailed explanation\nsrc/main.rs:22:12: 22:60 error: type mismatch resolving `for\u003c'r, 'r, 'r\u003e \u003c[closure@src/main.rs:22:25: 22:59 greeting:_] as core::ops::FnOnce\u003c(\u0026'r mut iron::request::Request\u003c'r, 'r\u003e,)\u003e\u003e::Output == core::result::Result\u003ciron::response::Response, iron::error::IronError\u003e`:\n expected bound lifetime parameter ,\n    found concrete lifetime [E0271]\nsrc/main.rs:22     router.post(\"/set\", |r| set_greeting(r, \u0026mut greeting));\n                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nsrc/main.rs:22:12: 22:60 help: run `rustc --explain E0271` to see a detailed explanation\nsrc/main.rs:21:12: 21:51 error: type mismatch: the type `[closure@src/main.rs:21:21: 21:50 greeting:\u0026Greeting]` implements the trait `core::ops::Fn\u003c(\u0026mut iron::request::Request\u003c'_, '_\u003e,)\u003e`, but the trait `for\u003c'r, 'r, 'r\u003e core::ops::Fn\u003c(\u0026'r mut iron::request::Request\u003c'r, 'r\u003e,)\u003e` is required (expected concrete lifetime, found bound lifetime parameter ) [E0281]\nsrc/main.rs:21     router.get(\"/\", |r| hello_world(r, \u0026greeting));\n                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nsrc/main.rs:21:12: 21:51 help: run `rustc --explain E0281` to see a detailed explanation\nsrc/main.rs:22:12: 22:60 error: the trait `for\u003c'r, 'r, 'r\u003e core::ops::Fn\u003c(\u0026'r mut iron::request::Request\u003c'r, 'r\u003e,)\u003e` is not implemented for the type `[closure@src/main.rs:22:25: 22:59 greeting:\u0026mut Greeting]` [E0277]\nsrc/main.rs:22     router.post(\"/set\", |r| set_greeting(r, \u0026mut greeting));\n                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nsrc/main.rs:22:12: 22:60 help: run `rustc --explain E0277` to see a detailed explanation\nerror: aborting due to 4 previous errors\nCould not compile `httptest`.\n```\n\nrustc 很不高兴。我也想到了。但我只是抛出类型，得到个响应。天啊，快告诉我搞定 rustc 吧。\n\n这些错误消息令人困惑，但显然闭包是个错误类型。这个`Router`的`get`和`post`方法拿一个[`Handler`](http://ironframework.io/doc/iron/middleware/trait.Handler.html)，且从 doc 页面中,我看到一个 impl 被定义为\n\n```rust\npub trait Handler: Send + Sync + Any {\n    fn handle(\u0026self, \u0026mut Request) -\u003e IronResult\u003cResponse\u003e;\n}\n```\n\n真是一口`**`，但是`Handler`定义为`Fn`，而不是`FnOnce`或`FnMut`，且必须为`Send + Sync`。 因为它需要发送(send)，所以我们不会捕获任何引用，并且由于环境不是可变的，所以我们必须使用内部可变性来改变`greeting`。所以我要使用可发送的智能指针，`Arc`，为了使它可变，加个`Mutex`在里面。为此，我们需要像这样从标准库中导入两者:`use std::sync::{Mutex, Arc};`。 我还要移动(move)捕获`move |r| ...`避免通过引用捕获。\n\n像这样更新我的代码，`MD`，还是会产生相同的错误消息。\n\n```rust\n    let greeting = Arc::new(Mutex::new(Greeting { msg: \"Hello, World\".to_string() }));\n    let greeting_clone = greeting.clone();\n\n    let mut router = Router::new();\n\n    router.get(\"/\", move |r| hello_world(r, \u0026greeting.lock().unwrap()));\n    router.post(\"/set\", move |r| set_greeting(r, \u0026mut greeting_clone.lock().unwrap()));\n```\n\nrustc 不喜欢我闭包的生命周期。为什么? 我不知道。我问 `#rust 的reem`，他是否知道该怎么做。\n\n几个小时后,reem 说\n\n```text\n16:02 \u003c reem\u003e brson: Partially hint the type of the request art, rustc has trouble inferring HRTBs\n\n16:02 \u003c reem\u003e brson: 请求(request) art 相关的部分类型, rustc 无法推断出 HRTBs\n```\n\nHRTB 意味着\"更高级别的特征界限(higher-ranked trait bounds)\",这也大概意味着\"复杂的生命周期\"。\n\n我将这些相同的行，更改为碰触到`r: \u0026mut Request`，最终一切归于平静……\n\n```rust\n    let greeting = Arc::new(Mutex::new(Greeting { msg: \"Hello, World\".to_string() }));\n    let greeting_clone = greeting.clone();\n\n    let mut router = Router::new();\n\n    router.get(\"/\", move |r: \u0026mut Request| hello_world(r, \u0026greeting.lock().unwrap()));\n    router.post(\"/set\", move |r: \u0026mut Request| set_greeting(r, \u0026mut greeting_clone.lock().unwrap()));\n```\n\n这似乎是 Rust 的推断器中的一个 bug。真蹩脚.\n\n现在它再次构建，所以我们可以用 curl 进行测试了.\n\n```text\n$ curl http://localhost:3000\n{\"msg\":\"Hello, World\"}\n$ curl -X POST -d '{\"msg\":\"Just trust the Rust\"}' http://localhost:3000/set\n$ curl http://localhost:3000\n{\"msg\":\"Just trust the Rust\"}\n```\n\n现在，我们正在掌握'雷电'.\n\n# 5. 客户端\n\n我们有了一个小 JSON 服务器。现在让我们编写客户端。这次我们将直接使用[Hyper].\n\n我把`hyper = \"*\"`加到`Cargo.toml`身上，然后创建`src/bin/client.rs`:\n\n```rust\nextern crate hyper;\n\nfn main() { }\n```\n\n`src/bin/`下的源文件，由 Cargo 自动构建为可执行文件。运行`cargo build`后，执行文件在`target/debug/client`出现。好了，‘老天’赐福。现在找出 Hyper 吧。\n\n拔下[Hyper 客户端的示例](http://hyperium.github.io/hyper/hyper/client/index.html)，我改了改这个片段，变成仅请求\"/\"，并打印 body:\n\n```rust\nextern crate hyper;\n\nuse hyper::*;\nuse std::io::Read;\n\nfn main() {\n    let client = Client::new();\n    let mut res = client.get(\"http://localhost:3000/\").send().unwrap();\n    assert_eq!(res.status, hyper::Ok);\n    let mut s = String::new();\n    res.read_to_string(\u0026mut s).unwrap();\n    println!(\"{}\", s);\n}\n```\n\n但是，现在`cargo run`又又又不工作啦.\n\n```text\n$ cargo run\n`cargo run` requires that a project only have one executable; use the `--bin` option to specify which one to run\n```\n\n我必须输入`cargo run --bin httptest`，启动服务器。这样做之后,运行`cargo run --bin client`,才看到:\n\n```text\n$ cargo run --bin client\nRunning `target/debug/client`\n{\"msg\":\"Hello, World\"}\n```\n\n哦伙计,看，我是个 Rust 巫师`:P`。最后一件事,我想做的是，发出 POST 请求来设置消息。显然要做的是改`client.get`为[`client.post`](http://hyperium.github.io/hyper/hyper/client/struct.Client.html#method.post)。 这会返回一个[`RequestBuilder`](http://hyperium.github.io/hyper/hyper/client/struct.RequestBuilder.html)，因此,我正在寻找设置有效负载的构建器方法。[`body`](http://hyperium.github.io/hyper/hyper/client/struct.RequestBuilder.html#method.body)怎么样?\n\n我的新创造:\n\n```rust\nextern crate hyper;\n\nuse hyper::*;\nuse std::io::Read;\n\nfn main() {\n    let client = Client::new();\n    let res = client.post(\"http://localhost:3000/set\").body(\"Just trust the Rust\").send().unwrap();\n    assert_eq!(res.status, hyper::Ok);\n    let mut res = client.get(\"http://localhost:3000/\").send().unwrap();\n    assert_eq!(res.status, hyper::Ok);\n    let mut s = String::new();\n    res.read_to_string(\u0026mut s).unwrap();\n    println!(\"{}\", s);\n}\n```\n\n但是运行它是令人失望的\n\n```text\n101 $ cargo run --bin client\n   Compiling httptest v0.2.0 (file:///Users/danieleesposti/workspace/httptest)\n     Running `target/debug/client`\nthread '\u003cmain\u003e' panicked at 'assertion failed: `(left == right)` (left: `InternalServerError`, right: `Ok`)', src/bin/client.rs:9\n```\n\n同时我看到服务器也出错:\n\n```text\n$ cargo run --bin httptest\n   Running `target/debug/httptest`\nthread '\u003cunnamed\u003e' panicked at 'called `Result::unwrap()` on an `Err` value: ParseError(SyntaxError(\"invalid syntax\", 1, 1))', src/libcore/result.rs:741\n```\n\n看来，是因为我不好的错误处理! 我没有将有效的 JSON 传递给`/set`路由。修正(fix) **body** 为 `.body(r#\"{ \"msg\": \"Just trust the Rust\" }\"#)`让客户端成功:\n\n```text\n$ cargo run --bin client\n   Compiling httptest v0.2.0 (file:///opt/dev/httptest)\n      Running `target/debug/client`\n{\"msg\":\"Just trust the Rust\"}\n```\n\n就像这样，我们在 Rust 中创建了一个 Web 服务和客户端。看来未来不远了。用[Iron]和[Hyper]去建立一些东西吧。\n\n[crater]: https://github.com/brson/taskcluster-crater\n[rust]: https://github.com/brson/taskcluster-crater\n[iron]: http://ironframework.io/\n[hyper]: http://hyperium.github.io/hyper/hyper/index.html\n[rustc-serialize]: https://crates.io/crates/rustc-serialize\n[serde]: https://crates.io/crates/serde\n[serde_macros]: https://crates.io/crates/serde_macros\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fhttptest-zh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchinanf-boy%2Fhttptest-zh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fhttptest-zh/lists"}