Ambassador

Build Status CocoaPods Code Climate Issue Count GitHub license

一个基于 SWSGI 的超轻量级 Swift Web 框架

特性

示例

这是一个如何使用 Ambassador 和 Embassy 作为 HTTP 服务器来模拟 API 端点的示例。

import Embassy
import EnvoyAmbassador

let loop = try! SelectorEventLoop(selector: try! KqueueSelector())
let router = Router()
let server = DefaultHTTPServer(eventLoop: loop, port: 8080, app: router.app)

router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
    return [
        ["id": "01", "name": "john"],
        ["id": "02", "name": "tom"]
    ]
}))

// Start HTTP server to listen on the port
try! server.start()

// Run event loop
loop.runForever()

然后你可以访问 http://[::1]:8080/api/v2/users 在浏览器中,或者使用 HTTP 客户端 GET 该 URL 并查看

[
  {
    "id" : "01",
    "name" : "john"
  },
  {
    "id" : "02",
    "name" : "tom"
  }
]

Router (路由)

Router 允许你将不同的路径映射到不同的 WebApp。 就像你在前面的例子中看到的那样,要将路径 /api/v2/users 路由到我们的响应处理程序,你只需将所需的路径设置为 WebApp 的值

let router = Router()
router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
    return [
        ["id": "01", "name": "john"],
        ["id": "02", "name": "tom"]
    ]
}))

并将 router.app 作为 SWSGI 接口传递给 HTTP 服务器。 当访问的路径未找到时,将使用 router.notFoundResponse,它只返回 404。 你可以覆盖 notFoundResponse 以自定义未找到时的行为。

你还可以使用正则表达式映射 URL。 例如,你可以这样写

let router = Router()
router["/api/v2/users/([0-9]+)"] = DelayResponse(JSONResponse(handler: { environ -> Any in
    let captures = environ["ambassador.router_captures"] as! [String]
    return ["id": captures[0], "name": "john"]
}))

然后,所有 URL 匹配 /api/v2/users/([0-9]+) 正则表达式的请求都将被路由到这里。 对于所有匹配组,它们将作为字符串数组传递到具有键 ambassador.router_captures 的环境中。

DataResponse (数据响应)

DataResponse 是一个用于发送数据的助手。 例如,假设你想创建一个端点来返回状态代码 500,你可以这样做

router["/api/v2/return-error"] = DataResponse(statusCode: 500, statusMessage: "server error")

状态默认为 200 OK,内容类型默认为 application/octet-stream,它们都可以通过 init 参数覆盖。 你还可以提供自定义标头和一个用于返回数据的处理程序。 例如

router["/api/v2/xml"] = DataResponse(
    statusCode: 201,
    statusMessage: "created",
    contentType: "application/xml",
    headers: [("X-Foo-Bar", "My header")]
) { environ -> Data in
    return Data("<xml>who uses xml nowadays?</xml>".utf8)
}

如果你希望以异步方式发送 body,你也可以使用另一个带有额外 sendData 函数作为参数的 init

router["/api/v2/xml"] = DataResponse(
    statusCode: 201,
    statusMessage: "created",
    contentType: "application/xml",
    headers: [("X-Foo-Bar", "My header")]
) { (environ, sendData) in
    sendData(Data("<xml>who uses xml nowadays?</xml>".utf8))
}

请注意,与 SWSGI 的 sendBody 不同,sendData 只需要用整个数据块调用一次。

JSONResponse (JSON 响应)

DataResponse 几乎相同,只是它接受 Any 而不是字节,并将该对象转储为 JSON 格式并响应你。 例如

router["/api/v2/users"] = JSONResponse() { _ -> Any in
    return [
        ["id": "01", "name": "john"],
        ["id": "02", "name": "tom"]
    ]
}

DelayResponse (延迟响应)

DelayResponse 是一个 **装饰器** 响应,它会延迟给定的响应一段时间。 在现实世界中,总是会有网络延迟,为了模拟延迟,DelayResponse 非常有帮助。 要延迟响应,只需执行

router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
    return [
        ["id": "01", "name": "john"],
        ["id": "02", "name": "tom"]
    ]
}))

默认情况下,它会随机延迟响应。 你可以通过传递 delay 参数来修改它。 比如,如果你想延迟 10 秒,你可以这样做

router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
    return [
        ["id": "01", "name": "john"],
        ["id": "02", "name": "tom"]
    ]
}), delay: .delay(10))

可用的延迟选项有

DataReader (数据读取器)

要从请求中读取 POST body 或任何其他 HTTP body,你需要使用 SWSGI 的 environ 参数中提供的 swsgi.input 函数。 例如,你可以这样做

router["/api/v2/users"] = JSONResponse() { environ -> Any in
    let input = environ["swsgi.input"] as! SWSGIInput
    input { data in
        // handle the data stream here
    }
}

这样做并不难,但是,数据以流的形式传入,例如

在大多数情况下,你不会喜欢手动处理数据流。 要等待所有数据接收完毕并一次性处理它们,你可以使用 DataReader。 例如

router["/api/v2/users"] = JSONResponse() { environ -> Any in
    let input = environ["swsgi.input"] as! SWSGIInput
    DataReader.read(input) { data in
        // handle the whole data here
    }
}

JSONReader (JSON 读取器)

DataReader 类似,除了读取整个数据块之外,JSONReader 还会将其解析为 JSON 格式。 这是你如何做的

router["/api/v2/users"] = JSONResponse() { environ -> Any in
    let input = environ["swsgi.input"] as! SWSGIInput
    JSONReader.read(input) { json in
        // handle the json object here
    }
}

URLParametersReader (URL 参数读取器)

URLParametersReader 等待接收所有数据,并将它们一次性解析为 URL 编码参数,如 foo=bar&eggs=spam。 这些参数将作为键值对数组 (String, String) 传递。

router["/api/v2/users"] = JSONResponse() { environ -> Any in
    let input = environ["swsgi.input"] as! SWSGIInput
    URLParametersReader.read(input) { params in
        // handle the params object here
    }
}

如果你想,你也可以使用 URLParametersReader.parseURLParameters 来解析 URL 编码的参数字符串。 像这样

let params = URLParametersReader.parseURLParameters("foo=bar&eggs=spam")

安装

CocoaPods

要使用 CocoaPod 安装,请将 Embassy 添加到你的 Podfile

pod 'EnvoyAmbassador', '~> 4.0'

Carthage

要使用 Carthage 安装,请将 Ambassador 添加到你的 Cartfile

github "envoy/Ambassador" ~> 4.0

请注意,你应该导入 Ambassador 而不是 EnvoyAmbassador。 我们使用 EnvoyAmbassador 来进行 Cocoapods,仅仅是因为名称 Ambassador 已经被占用。

Package Manager (包管理器)

要做到这一点,请将 repo 添加到 Package.swift,像这样

import PackageDescription

let package = Package(
    name: "AmbassadorExample",
    dependencies: [
        .package(url: "https://github.com/envoy/Ambassador.git",
                 from: "4.0.0"),
    ]
)