Swiftgger 是一个 Swift 库,可以生成与 OpenAPI 3.0.1 版本 兼容的输出。您可以使用 Swiftgger 类来描述您的 API,并通过 API 中的端点公开 OpenAPI 定义。该端点的 URL 可以用于 Swagger UI。
您还可以使用 Swiftgger 基于 OpenAPI 定义文件生成 Swift 文件。用法示例
$ swiftgger-generator -u http://:8000/openapi.json -o ../output
以上命令将生成包含模型类和 HTTP 客户端服务的 Swift 文件。
此功能目前正在开发中。
Swiftgger 支持 Swift Package Manager。您必须将关于 Swiftgger 的信息添加到您的 Package.swift
文件中。以下是一个简单的示例。
let package = Package(
name: "YourApp",
dependencies: [
.package(url: "https://github.com/mczachurski/Swiftgger.git", from: "1.4.0")
],
targets: [
.target(name: "YourApp", dependencies: ["Swiftgger"]),
.testTarget(name: "YourAppTests", dependencies: ["YourApp"])
]
)
Swiftgger 需要至少 Swift 5.3 版本。
不幸的是,Swift 在反射(内省)方面并不完美,我们必须手动完成许多设置。
OpenAPIBuilder
是主要对象,负责收集关于我们 API 结构的信息并生成 OpenAPI 响应。它包含一些关于 API 的基本信息,如标题、版本、作者、许可证等。
let openAPIBuilder = OpenAPIBuilder(
title: "Tasker server API",
version: "1.0.0",
description: "This is a sample server for task server application.",
termsOfService: "http://example.com/terms/",
contact: APIContact(name: "John Doe", email: "john.doe@some-email.org", url: URL(string: "http://example-domain.com/@john")),
license: APILicense(name: "MIT", url: URL(string: "http://mit.license")),
authorizations: [.jwt(description: "You can get token from *sign-in* action from *Account* controller.")]
)
如果我们想要指定控制器和操作的列表,我们可以使用 openAPIBuilder
对象。
添加关于控制器的信息非常简单。我们必须在 OpenAPIBuilder
对象上执行 add
方法。
openAPIBuilder.add(
APIController(name: "Users", description: "Controller where we can manage users", actions: [])
)
每个控制器都可以有一个操作(路由)列表,包含名称、描述、响应和请求信息。
按 id 获取操作(响应中的对象)
APIAction(method: .get, route: "/users/{id}",
summary: "Getting user by id",
description: "Action for getting specific user from server",
parameters: [
APIParameter(name: "id", description: "User id", required: true)
],
responses: [
APIResponse(code: "200", description: "Specific user", type: .object(UserDto.self),
APIResponse(code: "404", description: "User with entered id not exists"),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
)
获取操作(响应中的值类型)
APIAction(method: .get, route: "/version",
summary: "Getting system version",
description: "Action for getting application current version",
responses: [
APIResponse(code: "200", description: "Specific user", type: .value("1.0.0")
]
)
Post 操作
APIAction(method: .post, route: "/users",
summary: "Adding new user",
description: "Action for adding new user to the server",
request: APIRequest(type: .object(UserDto.self), description: "Object with user information."),
responses: [
APIResponse(code: "200", description: "User data after adding to the system", type: .object(UserDto.self)),
APIResponse(code: "400", description: "There was issues during adding new user", type: .object(ValidationErrorResponseDto.self)),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
)
除了控制器和操作之外,我们还必须指定可以在 API 中使用的对象列表。我们可以像下面的代码片段中那样做。
openAPIBuilder.add([
APIObject(object: UserDto(id: UUID(), createDate: Date(), name: "John Doe", email: "email@test.com", isLocked: false)),
APIObject(object: ValidationErrorResponseDto(message: "Object is invalid", errors: ["property": "Information about error."]))
])
下面是如何配置完整 CRUD 操作的示例。当然,在该示例中,整个配置在一个地方完成。但是,在您的应用程序中,您可以将端点/操作配置放在您的实现附近(每个操作分开)。
// Create builder.
let openAPIBuilder = OpenAPIBuilder(
title: "Tasker server API",
version: "1.0.0",
description: "This is a sample server for task server application.",
authorizations: [.jwt(description: "You can get token from *sign-in* action from *Account* controller.")]
)
.add(APIController(name: "Users", description: "Controller where we can manage users", actions: [
APIAction(method: .get, route: "/users",
summary: "Getting all users",
description: "Action for getting all users from server",
responses: [
APIResponse(code: "200", description: "List of users", type: .object(UserDto.self)),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
),
APIAction(method: .get, route: "/users/{id}",
summary: "Getting user by id",
description: "Action for getting specific user from server",
parameters: [
APIParameter(name: "id", description: "User id", required: true)
],
responses: [
APIResponse(code: "200", description: "Specific user", type: .object(UserDto.self)),
APIResponse(code: "404", description: "User with entered id not exists"),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
),
APIAction(method: .post, route: "/users",
summary: "Adding new user",
description: "Action for adding new user to the server",
request: APIRequest(type: .object(UserDto.self), description: "Object with user information."),
responses: [
APIResponse(code: "200", description: "User data after adding to the system", type: .object(UserDto.self)),
APIResponse(code: "400", description: "There was issues during adding new user", type: .object(ValidationErrorResponseDto.self)),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
),
APIAction(method: .put, route: "/users/{id}",
summary: "Updating user",
description: "Action for updating specific user in the server",
parameters: [
APIParameter(name: "id", description: "User id", required: true)
],
request: APIRequest(type: .object(UserDto.self), description: "Object with user information."),
responses: [
APIResponse(code: "200", description: "User data after adding to the system", type: .object(UserDto.self)),
APIResponse(code: "400", description: "There was issues during updating user", type: .object(ValidationErrorResponseDto.self)),
APIResponse(code: "404", description: "User with entered id not exists"),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
),
APIAction(method: .delete, route: "/users/{id}",
summary: "Deleting user",
description: "Action for deleting user from the database",
parameters: [
APIParameter(name: "id", description: "User id", required: true)
],
responses: [
APIResponse(code: "200", description: "User was deleted"),
APIResponse(code: "404", description: "User with entered id not exists"),
APIResponse(code: "401", description: "User not authorized")
],
authorization: true
)
])
)
.add([
APIObject(object: UserDto(id: UUID(), createDate: Date(), name: "John Doe", email: "email@test.com", isLocked: false)),
APIObject(object: ValidationErrorResponseDto(message: "Object is invalid", errors: ["property": "Information about error."]))
])
当您为所有控制器/操作准备好配置后,您必须执行以下代码
let document = try openAPIBuilder.built()
对象 document
存储关于您的 API 的信息,并且它与 OpenAPI 标准兼容。现在您必须将该对象序列化为 JSON,并通过您的 API 应用程序中的附加端点公开它。该 JSON(端点)可以被任何 OpenAPI 兼容的客户端应用程序使用。
Swagger UI 是一个很棒的工具,可以可视化请求模型、参数等。
您还可以清楚地了解您的端点可能返回的响应列表。
您可以在我的另一个 GitHub 项目中找到更多示例。
Tasker 服务器 OpenAPI JSON: https://taskerswift.azurewebsites.net/openapi
Tasker 服务器 Swagger UI: https://taskerswift-swagger.azurewebsites.net/
swiftgger-generator
是一个简单的应用程序,可以基于 OpenAPI 定义生成 Swift 文件。应用程序为每个控制器(组)生成模型类和 HTTP 客户端服务的文件。命令行参数
swiftgger-generator: [command_option] [-f jsonFile] [-u url] [-o path]")
Command options are:
-h show this message and exit
-v show program version and exit
-f input .json file with OpenAPI description
-u input URL which returns .json with OpenAPI description
-o output directory (default is 'output')
待办事项
本项目根据 MIT 许可证的条款获得许可。