24 个版本

使用旧的 Rust 2015

0.10.0 2016 年 12 月 8 日
0.9.0 2016 年 8 月 14 日
0.8.0 2015 年 10 月 31 日
0.7.3 2016 年 4 月 4 日
0.1.0 2014 年 11 月 29 日

#1408 in HTTP 服务器

MIT 许可证

105KB
2K SLoC

Gitter

目录

什么是 Rustless?

Build Status

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"

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(&params.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应用程序:ApiNamespaceEndpoint

  • 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| {
                        // ...
                    );
                })
            })
        })
    })
})

参数验证和转换

您可以使用EndpointNamespace定义内部的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状态码。您可以使用statusset_status查询和设置实际的HTTP状态码

client.set_status(NotFound);

使用参数

请求参数可通过Endpoint处理程序和所有回调中的params: JsonObject获取。这包括GETPOSTPUT参数,以及您在路由字符串中指定的任何命名参数。

请求

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)
    })
});

在路由字符串参数和

  • 冲突的情况下
  • GETPOSTPUT 参数
  • POSTPUT 请求体的内容

路由字符串参数将具有优先级。

重定向

您可以临时重定向到新的 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
    }
});

前后回调

可以使用 beforeafterbefore_validationafter_validation 在每次 API 调用之前或之后执行代码块。

前后回调按以下顺序执行

  1. before
  2. before_validation
  3. 验证
  4. after_validation
  5. API 调用
  6. 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