跳转至

Vapor模板工程文件结构

目录总体结构

Bash
$ tree -L 1 .
.
├── Dockerfile
├── Package.resolved
├── Package.swift
├── Public
├── Sources
├── Tests
└── docker-compose.yml

3 directories, 4 files
  • Package.resolved解析Package.swift时自动生成的,开发者不需要修改
  • Package.swift用来定义一个项目的依赖和生成产物信息
  • Public用来存放公共资源文件,包括:图片、CSS样式表、js文件以及Leaf模板文件
  • Sources用来存放整个工程的主体源代码文件
  • Tests用来存放针对工程功能所写的测试用例的代码文件
  • Dockerfiledocker-compose.yml部署到docker容器中执行的配置文件

Sources子目录

Bash
$ tree Sources/
Sources
├── App
   ├── Controllers
   ├── configure.swift
   └── routes.swift
└── Run
    └── main.swift

3 directories, 3 files

Source目录下的每一个子目录都是项目的一个模块。App是一个功能模块,Run是一个可执行模块,Run模块编译后可被操作系统调起运行,App模块不可单独运行,它被Run模块依赖。

整个程序的运行入口是main.swift文件。Package.swift文件中描述了整体工程是如何构成的:

Package.swift 项目描述文件

Swift
// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "HelloVapor",
    platforms: [
    .macOS(.v12)
    ],
    dependencies: [
        // 💧 A server-side Swift web framework.
        .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
    ],
    targets: [
        .target(
            name: "App",
            dependencies: [
                .product(name: "Vapor", package: "vapor")
            ],
            swiftSettings: [
                // Enable better optimizations when building in Release configuration. Despite the use of
                // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
                // builds. See <https://github.com/swift-server/guides/blob/main/docs/building.md#building-for-production> for details.
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
            ]
        ),
        .executableTarget(name: "Run", dependencies: [.target(name: "App")]),
        .testTarget(name: "AppTests", dependencies: [
            .target(name: "App"),
            .product(name: "XCTVapor", package: "vapor"),
        ])
    ]
)

Package.swift描述文件中可以看出,每一个Target定义了一个模块,App模块依赖了一个第三方的包: vapor中的Vapor模块,这个被依赖的包的信息在上面的dependencies数组中指定,SPM会解析它,并拉取相关的文件到本地参与工程编译。

Run可执行模块依赖了App功能模块,App功能模块又依赖了其它第三方提供的功能模块

AppTests模块依赖了App模块,因为它是针对App专门写的测试模块,通过运行一个个测试用例,来测试App模块的各个功能是否正常。

Tests 子目录

Bash
$ tree Tests
Tests
└── AppTests
    └── AppTests.swift

1 directory, 1 file

AppTests.swift中可以编写测试App模块的测试用例。


示例工程的代码逻辑

main.swift
Swift
import App
import Vapor

var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)
let app = Application(env)
defer { app.shutdown() }
try configure(app)
try app.run()

main.swift为程序运行的入口,获取到命令行参数以及一些环境变量,用这些信息去创建app,在app运行之前,使用configure.swift文件中的函数configure对app实例进行配置

configure.swift
Swift
import Vapor

// configures your application
public func configure(_ app: Application) throws {
    // uncomment to serve files from /Public folder
    // app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))

    // register routes
    try routes(app)
}

app实例配置过程中调用routes.swift中的方法,对app路由进行配置。

routes.swift
Swift
import Vapor

func routes(_ app: Application) throws {
    app.get { req async in
        "It works!"
    }

    app.get("hello") { req async -> String in
        "Hello, world!"
    }
}

routes函数中为app实例配置了两个GET类型的路由: //hello,在HelloVapor工程根目录下使用vapor run或者swift run命令编译运行程序后,即可以浏览器中测试对应的路由是否正常工作


测试子模块

AppTests.swift
Swift
@testable import App
import XCTVapor

final class AppTests: XCTestCase {
    func testHelloWorld() throws {
        let app = Application(.testing)
        defer { app.shutdown() }
        try configure(app)

        try app.test(.GET, "hello", afterResponse: { res in
            XCTAssertEqual(res.status, .ok)
            XCTAssertEqual(res.body.string, "Hello, world!")
        })
    }
}

AppTests.swift针对App模块写测试用例,使用了XCTest框架,每一个测试用例方法的名称都以test开头。在工程根目录下使用swift test命令运行测试用例。

在Mac上使用Xcode进行Vapor项目的开发

在项目根目录下运行vapor xcode,使用Xcode打开后,运行程序或者执行测试。

  • Cmd+R 运行程序
  • Cmd+U 执行测试

vapor-xcode-test

评论