24 个版本
使用旧的 Rust 2015
0.10.0 | 2016 年 12 月 8 日 |
---|---|
0.9.0 | 2016 年 8 月 14 日 |
0.8.0 |
|
0.7.3 | 2016 年 4 月 4 日 |
0.1.0 | 2014 年 11 月 29 日 |
#1408 in HTTP 服务器
105KB
2K SLoC
目录
- 什么是 Rustless?
- 使用警告
- 基本用法
- 复杂示例
- 挂载
- 参数验证和转换
- 使用 JSON Schema
- 查询字符串
- API 版本控制
- 以自定义 HTTP 状态码响应
- 使用参数
- 重定向
- 错误触发
- 错误处理
- 前后回调
- 安全 API 示例
- JSON 响应
- Swagger 2.0 支持
- 与 PostgreSQL 集成
- 与 Deuterium ORM 集成
什么是 Rustless?
Rustless 是一个用于 Rust 的类似 REST 的 API 微框架。它旨在提供一个简单的 DSL,以便在 Iron 网页框架之上轻松开发 RESTful API。它内置了对常见约定的支持,包括多种格式、子域/前缀限制、内容协商、版本控制等。
Rustless 是将 Ruby 世界的 Grape 库移植到 Rust。基于 hyper - 一个 Rust 的 HTTP 库。
与 Rust 本身一样,Rustless 目前仍处于早期开发阶段,因此如果 API 发生变化或出现故障,请不要感到惊讶。如果某些功能不正常,请提交问题或发送拉取请求!
# Cargo.toml
[dependencies.rustless]
git = "https://github.com/rustless/rustless"
另请参阅
- Valico - Rust JSON 验证器和转换器。请参阅 API 文档。
- Queryst - 支持嵌套的 Rust 查询字符串解析器。请参阅 API 文档。
- JsonWay - JSON 构建 DSL 和可配置的 Rust 序列化器。请参阅 API 文档。
使用警告
Rustless 基于 Iron,Iron 基于 Hyper,而 Hyper 是 同步的。Hyper 目前有很多限制,无法处理许多并发连接,尤其是在 keep-alive 情况下。因此,强烈建议使用轻量级异步网页服务器,如 Nginx,作为 Rustless 的反向代理服务器。
基本用法
以下是一个简单的示例,展示了 Rustless 的一些更常见的功能。
#[macro_use]
extern crate rustless;
extern crate hyper;
extern crate iron;
extern crate rustc_serialize as serialize;
extern crate valico;
use valico::json_dsl;
use hyper::status::StatusCode;
use rustless::{
Application, Api, Nesting, Versioning
};
use rustless::json::ToJson;
fn main() {
let api = Api::build(|api| {
// Specify API version
api.version("v1", Versioning::AcceptHeader("chat"));
api.prefix("api");
// Create API for chats
api.mount(Api::build(|chats_api| {
chats_api.after(|client, _params| {
client.set_status(StatusCode::NotFound);
Ok(())
});
// Add namespace
chats_api.namespace("chats/:id", |chat_ns| {
// Valico settings for this namespace
chat_ns.params(|params| {
params.req_typed("id", json_dsl::u64())
});
// Create endpoint for POST /chats/:id/users/:user_id
chat_ns.post("users/:user_id", |endpoint| {
// Add description
endpoint.desc("Update user");
// Valico settings for endpoint params
endpoint.params(|params| {
params.req_typed("user_id", json_dsl::u64());
params.req_typed("id", json_dsl::string())
});
endpoint.handle(|client, params| {
client.json(¶ms.to_json())
})
});
});
}));
});
let app = Application::new(api);
iron::Iron::new(app).http("0.0.0.0:4000").unwrap();
println!("On 4000");
println!("Rustless server started!");
}
复杂示例
如果您想了解如何使用Rustless编写一些复杂的应用程序,请参阅示例。
在示例中,请注意以下方面
- 带有版本号的复杂嵌套API。
- 使用rust-postgres进行CRUD操作。
- Swagger 2.0集成。
- JSON Schema验证。
- 错误报告。
- 序列化器。
- 文件结构。
- 与docopt集成。
- 与deuterium-orm集成。数据库迁移。
挂载
在Rustless中,您可以使用三个核心实体来构建您的RESTful应用程序:Api
、Namespace
和Endpoint
。
- Api可以挂载Api、Namespace和Endpoint
- Namespace可以挂载Api、Namespace和Endpoint
Api::build(|api| {
// Api inside Api example
api.mount(Api::build(|nested_api| {
// Endpoint definition
nested_api.get("nested_info", |endpoint| {
// endpoint.params(|params| {});
// endpoint.desc("Some description");
// Endpoint handler
endpoint.handle(|client, _params| {
client.text("Some usefull info".to_string())
})
});
}))
// The namespace method has a number of aliases, including: group,
// resource, resources, and segment. Use whichever reads the best
// for your API.
api.namespace("ns1", |ns1| {
ns1.group("ns2", |ns2| {
ns2.resource("ns3", |ns3| {
ns3.resources("ns4", |ns4| {
ns4.segment("ns5", |ns5| {
// ...
);
})
})
})
})
})
参数验证和转换
您可以使用Endpoint
和Namespace
定义内部的DSL块来定义您的参数验证和强制选项。有关更多信息,请参阅Valico。
api.get("users/:user_id/messages/:message_id", |endpoint| {
endpoint.params(|params| {
params.req_typed("user_id", Valico::u64());
params.req_typed("message_id", Valico::u64());
});
// ...
})
使用 JSON Schema
您还可以使用JSON Schema(IETF的v4草案)来验证您的参数。要在应用程序中使用模式,您需要以下设置
use valico::json_schema;
use rustless::batteries::schemes;
let scope = json_schema::Scope::new();
// ... You can insert some external schemes here ...
schemes::enable_schemes(&mut app, scope).unwrap();
有关在DSL块中使用JSON Schema的更多信息,请参阅Valico。
查询字符串
Rustless与queryst集成,以允许智能查询字符串解析和编码(即使是嵌套的,如foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb
)。有关更多信息,请参阅queryst。
API 版本控制
客户端可以通过以下三种策略访问您的API端点
- 路径
- AcceptHeader
- 参数
路径版本化策略
api.version("v1", Path);
使用此版本化策略,客户端应在URL中传递所需版本。
curl -H https://127.0.0.1:3000/v1/chats/
头部版本化策略
api.version("v1", AcceptHeader("chat"));
使用此版本化策略,客户端应在HTTP Accept
头部中传递所需版本。
curl -H Accept:application/vnd.chat.v1+json https://127.0.0.1:3000/chats
Accept版本格式与GitHub(使用)相同https://developer.github.com/v3/media/。
参数版本化策略
api.version("v1", Param("ver"));
使用此版本化策略,客户端应在URL查询中作为请求参数传递所需版本。
curl -H https://127.0.0.1:9292/statuses/public_timeline?ver=v1
以自定义 HTTP 状态码响应
默认情况下,Rustless为GET
请求返回200状态码,为POST
请求返回201状态码。您可以使用status
和set_status
查询和设置实际的HTTP状态码
client.set_status(NotFound);
使用参数
请求参数可通过Endpoint
处理程序和所有回调中的params: JsonObject
获取。这包括GET
、POST
和PUT
参数,以及您在路由字符串中指定的任何命名参数。
请求
curl -d '{"text": "hello from echo"}' 'https://127.0.0.1:3000/echo' -H Content-Type:application/json -v
Rustless端点
api.post("", |endpoint| {
endpoint.handle(|client, params| {
client.json(params)
})
});
在路由字符串参数和
- 冲突的情况下
GET
、POST
和PUT
参数POST
和PUT
请求体的内容
路由字符串参数将具有优先级。
重定向
您可以临时重定向到新的 URL(302)或永久重定向(301)。
client.redirect("http://google.com");
client.redirect_permanent("http://google.com");
错误触发
您可以通过抛出 error
来终止 API 方法的执行。
定义您的错误如下
use rustless::errors::{Error, ErrorRefExt};
#[deriving(Show)]
pub struct UnauthorizedError;
impl std::error::Error for UnauthorizedError {
fn description(&self) -> &str {
return "UnauthorizedError";
}
}
然后抛出
client.error(UnauthorizedError);
错误处理
默认情况下,Rustless 将使用 status::InternalServerError 响应所有错误。
Rustless 可以告知捕获特定错误并以自定义 API 格式返回它们。
api.error_formatter(|err, _media| {
match err.downcast::<UnauthorizedError>() {
Some(_) => {
return Some(Response::from_string(StatusCode::Unauthorized, "Please provide correct `token` parameter".to_string()))
},
None => None
}
});
前后回调
可以使用 before
、after
、before_validation
和 after_validation
在每次 API 调用之前或之后执行代码块。
前后回调按以下顺序执行
before
before_validation
- 验证
after_validation
- API 调用
after
步骤 4、5 和 6 仅在验证成功时发生。
该代码块适用于当前嵌套级别及其以下的所有 API 调用。
安全 API 示例
Api::build(|api| {
api.prefix("api");
api.version("v1", Versioning::Path);
api.error_formatter(|err, _media| {
match err.downcast::<UnauthorizedError>() {
Some(_) => {
return Some(Response::from_string(StatusCode::Unauthorized, "Please provide correct `token` parameter".to_string()))
},
None => None
}
});
api.namespace("admin", |admin_ns| {
admin_ns.params(|params| {
params.req_typed("token", Valico::string())
});
// Using after_validation callback to check token
admin_ns.after_validation(|&: _client, params| {
match params.get("token") {
// We can unwrap() safely because token in validated already
Some(token) => if token.as_string().unwrap().as_slice() == "password1" { return Ok(()) },
None => ()
}
// Fire error from callback is token is wrong
return Err(Box::new(UnauthorizedError) as Box<Error>)
});
// This `/api/admin/server_status` endpoint is secure now
admin_ns.get("server_status", |endpoint| {
endpoint.handle(|client, _params| {
{
let cookies = client.request.cookies();
let signed_cookies = cookies.signed();
let user_cookie = Cookie::new("session".to_string(), "verified".to_string());
signed_cookies.add(user_cookie);
}
client.text("Everything is OK".to_string())
})
});
})
})
JSON 响应
Rustless 包含 JsonWay 库,提供复杂的 JSON 构建域特定语言(DSL)和可配置的对象序列化器。请参阅 API 文档 了解详细信息。
您也可以自由地使用任何其他序列化库。
Swagger 2.0
Rustless 实现了 Swagger 2.0 规范的基本版本。它尚未完全完成,未来我们需要实现
- JSON Schema 支持(当出现适当的 JSON Schema 库时);
- 规范的安全部分;
但现在您已经可以使用 Swagger 2.0 了
let mut app = rustless::Application::new(rustless::Api::build(|api| {
// ...
api.mount(swagger::create_api("api-docs"));
// ...
}))
swagger::enable(&mut app, swagger::Spec {
info: swagger::Info {
title: "Example API".to_string(),
description: Some("Simple API to demonstration".to_string()),
contact: Some(swagger::Contact {
name: "Stanislav Panferov".to_string(),
url: Some("http://panferov.me".to_string()),
..std::default::Default::default()
}),
license: Some(swagger::License {
name: "MIT".to_string(),
url: "http://opensource.org/licenses/MIT".to_string()
}),
..std::default::Default::default()
},
host: "localhost:4000".to_string(),
..std::default::Default::default()
});
之后,您可以使用 Swagger UI 中的 /api-docs
路径来渲染您的 API 结构。
与 PostgreSQL 集成
我们在 postgres_example 中有一个此类集成的示例。请尝试它,并自由地提出您的意见。
与 Deuterium ORM 集成
TODO:示例
依赖项
~12MB
~272K SLoC