本文介绍了如何在 mac 10.5.4 和 ubuntu 18.04 下安装 Vapor 命令行工具,创建 Vapor 项目,简单的调试项目以及如何部署到生产环境的过程。文中的运行环境为:Vapor 4, Swift 5.2, Catalina 10.5.4, Ubuntu 18.04
最近新开通了一个云服务器(Linux 系统),使用 Hexo 创建了新的博客网页。考虑增加点简单的交互功能,所以需要在服务器端添加逻辑处理能力。由于近半年来基本上都是在使用 Swift,所以打算尝试一下 Server Side Swift。没有太仔细选择各种框架,看过几个介绍 Vapor 使用的视频后,感觉不错,便开始尝试安装配置。
Vapor 的官方文档实在是有很大的问题,网上的不少心得、教程又有点陈旧(目前版本调整较大)。通过两天的折腾,终于基本上理出了脉络,初步搭建了开发和生产环境。
什么是 Vapor
Vapor 是一个使用 Swift 语言编写的 Web 网络框架,它是跨平台的(mac、Linux),用户可以使用 Swift 语言以及其丰富的第三方库来高效的完成多种网络服务。
import Vapor
let app = try Application()
let router = try app.make(Router.self)
router.get("hello") { req in
return "Hello, world."
}
try app.run()上面的代码变可以完成一个最基本的网络服务。访问 http://localhost:8080/hello 网页返回 hello, world.
你可以在 mac 或 Linux 平台上开发,同时也可以将通过 Vapor 框架开发的项目部署到 mac 或 Linux 平台上。
什么是 Vaper 命令行工具
Vaper 命令行工具的作用有:
- 基于模板创建 Vaper 项目
- 配置、编译、运行项目
- 其他配合操作系统的一些功能
但它不是必须的,如果用户已经很熟悉 Vapor 的开发和配置,无论是在开发环境还是运行环境都可以不使用这个命令行工具。不过对于像我这样的新手来说,无疑它是一个好的帮手。
开发 Vaper 项目需要的资源
- mac 或 Linux ,我目前在 Catalina 10.5.4 和 Ubuntu 18.04 下都成功完成了配置
- Swift 语言环境,目前 Vapor 4 可以在 Swift 5.2 下运行
- web 服务器,我目前使用 Nginx (如果只是用于开发测试,也可以不配置)
安装 Swift
Mac
在 mac 平台下,安装 Xcode 及 Xcode ommand Line Tools,Vapor 4 对 xcode 提供了非常友好的支持,可以像其他的 Swift 项目一样使用全部的 Xcode 的各种能力(比如断点调试)。
Ubuntu 18.04
sudo apt-get install clang
sudo apt-get install libcurl3 libpython2.7 libpython2.7-dev
#从 swift.org 找到需要的文件
wget https://swift.org/builds/swift-5.2.3-release/ubuntu1804/swift-5.2.3-RELEASE/swift-5.2.3-RELEASE-ubuntu18.04.tar.gz
tar xzvf swift-5.2.3-RELEASE-ubuntu18.04.tar.gz
sudo mv swift-5.2.3-RELEASE-ubuntu18.04 /usr/share/swift
echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bashrc
source ~/.bashrc我也使用过 Docker 安装 Swift 5.2,不过 Image 有点太大,需要 1.2 Gb 左右的空间。
#Docker 安装 Swift 的方法。
docker pull swift安装 Vapor 命令行工具
MacOS
brew tap vapor/tap
brew install vapor
#我目前安装的是 vapor-beta
#brew install vapor-beta
#执行 vapor 测试一下
vaporUbuntu 1804
Ubuntu 下安装 Vapor toolbox 略微麻烦,主要是目前的 Vapor Toolbox 源文件有点问题需要做一些修改才能正常编译。
首先保证已成功安装 Swift
cd ~
git clone https://github.com/vapor/toolbox.git
cd toolbox
在 Test 目录下创建一个 LinuxMain. swift 文件
import XCTest
@testable import AppTests
XCTMain([testCase(AppTests.allTests)])这个是 Swift SPM 需要的文件,上面我只写了个最简单的能够完成编译即可,不清楚为什么官方的 git 源不包含这个文件。
修改 Source/VaporToolbox/exec. swift 文件
在 36 行左右(当前版本)找到
let spawned = posix_spawnp(&pid, argv[0], &fileActions, nil, argv + [nil], envp + [nil])修改成
guard let _argv0 = argv[0] else {
fatalError("unwrap error")
}
let spawned = posix_spawnp(&pid, _argv, &fileActions, nil, argv + [nil], envp + [nil])
同样不清楚为什么代码会有这么一个错误。
完成上述修改后
cd ~/toolbox
swift build -c release --disable-sandbox
sudo mv .build/release/vapor /usr/local/bin以上步骤在我本地的 Ubuntu 上已可正常编译,不过在我的腾讯云主机上,编译时会缺少一个相关库,添加上后即可正常编译。
sudo apt-get install libcurl4 -y至此 Vapor Toolbox 安装完毕。
Toolbox 的目录中包含了 Dockerfile,使用它可以直接将 toolbox 生成一个 Docker Image,并且系统会自动下载 Swift 的 Docker Image。不过我在 Ubuntu 上使用这种方法安装后(已配置 entrypoint),vapor 的 Image 没有名字,只有 container id,但是可以通过 id 来运行。个人目前不推荐这种方式。
使用 Vapor 命令行工具
创建项目
#vapor new <projectname> [--template]
vapor new hello使用缺省模板创建一个名为 hello 的 Vapor 项目。
创建的过程其实就是从 github 上 clone 一个模板,并可帮你进行简单配置。如果已经比较熟悉的话也可以不适用工具,直接从 github 上 克隆模板 开始项目。
在 Mac 系统下,模板可以直接编译运行,不过 Linux 下,git 源仍然缺少 LinuxMain. swift 文件,按照上面的方法,在项目中加入该文件后方可编译。
clone 完成后,系统会有如下提示:
Would you like to use Fluent? y
数据库类型选择 #我选择了 sqllite系统会根据你的选择直接在模板中创建好相应的代码。(Fluent 是一个 Swift 写的 ORM)
编译项目
cd ~/hello
vapor build我在 ubuntu 下执行** vapor new 可以正常执行,不过 vapor build 执行报错。因此就直接使用 swift build 来编译项目。其实 build** 和 run 都是直接调用的 swift 命令。
运行项目
vapor run
#系统显示,说明项目已正常启动。可以通过 http://127.0.0.1:8080 访问
Environment(name: "development", arguments: [".build/x86_64-apple-macosx/debug/Run"])
[ NOTICE ] Server starting on http://127.0.0.1:8080可以在运行命令后面添加运行状态,对于部署尤为重要。
vapor run --env prod
# test prod dev 对应不同的状态,主要关系到是否显示操作日志等如果是 Mac 系统,使用
vapor xcode 直接打开 Xcode,然后就可以直接在 Xcode 下编辑、编译、调试、运行。
即使没有安装 Vapor Toolbox 也可以使用如下命令创建 Xcode 项目
swift package generate-xcodeproj按照上述的步骤安装后,无论在 Mac 上还是 Ubuntu 上我们都可以开始编写并运行自己的 Vapor 项目了。
模板项目简单分析
本节我们通过对模板代码的简单分析来快速感受一下 Vapor 的便捷。
我使用的是项目缺省模板,启用 Fluent,数据库选择的是 sqlite。
项目源文件存在 Sources 目录下。
main.swift 作为程序的入口,创建了 Vapor 服务
configure.swift 中由于我们选择了使用 sqlite,因此系统为我们自动生成了使用数据库所需的代码。下列代码完成数据库的创建工作。
app.migrations.add(CreateTodo())为了能够完整的运行这个模板项目,我们需要在命令行执行
vapor run migrate系统将在项目根目录下完成 db. sqlite 里的表创建工作。如果没有执行这个步骤,访问 localhost: 8080/todos 将在得到如下的错误提示。
[ ERROR ] error: no such table: todosXcode 的用户也可以直接在 Scheme 中的 Arguments 添加 —auto-migrate 完成上述功能。
另外,最好在 Xcode 中将 Scheme — Run — Working Directory 设置成当前项目的根目录,这样无论使用命令行,还是直接使用 Xcode 都可以使用同一个 Sqlite 文件。
//app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))如果取消这一句的注释,Vapor 将提供对静态文件的支持。我们可以把静态文件放入项目根目录的 Public 目录中,即可访问。 127.0.0.1:8080/index. html 。如果和其他 WebServer 配合使用的话,我感觉还是用如 Nginx 来提供静态文件的支持比较好。
routes. swift 作为项目的核心,在其中完成网络的路由逻辑设定。
import Fluent
import Vapor
func routes(_ app: Application) throws {
/*
访问 127.0.0.1:8080/ 返回:It works!
*/
app.get { req in
return "It works!"
}
// localhost:8080/hello 返回:"Hello world"
app.get("hello") { req -> String in
return "Hello, world!"
}
let todoController = TodoController()
/*
以下操作我使用 postman 进行测试
post 127.0.0.1:8080/todos body 内容:{"title":"东坡肘子"} 添加一条记录
get 127.0.0.1:8080/todos 显示已添加的记录
del 127.0.0.1:8080/todos/B508471F-FF5F-422C-B384-C300FD7B49D9 删除一条记录。id 使用显示记录获取
*/
app.get("todos", use: todoController.index)
app.post("todos", use: todoController.create)
app.delete("todos", ":todoID", use: todoController.delete)
}
更具体的应用就不展开了。不过仅从模板例程上我们便可以感觉到 Vapor 的便利和高效。
和 Nginx 配合使用
通过编辑 nginx 的配置文件,我们的 Vapor 项目便可以对外发布了。
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri @proxy;
}
location @proxy {
proxy_pass http://127.0.0.1:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}现在你就可以通过 http://你的域名或地址/todos 来访问你的项目。
我使用 Vapor 的目的是为了配合自己的博客,所以仍需配合我自己原有的页面使用,所以采用了如下的配置。
即使打开了 Vapor 的静态页面支持,如果我把 Vapor 项目配置在/的话(已取消 Vapor 对根的响应),仍然需要明确的输入 http://我的域名/index.html 才能访问到索引页面。没有办法才把他转到 /api/下。
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /api {
root html;
index index.html index.htm;
try_files $uri @proxy;
}
location @proxy {
proxy_pass http://127.0.0.1:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}如此配置后,需要对代码 routes. swift 进行改动后即可正常访问。
app.get("api","hello") { req -> String in
return "Hello, world!"
}如果谁知道如何设置能够直接将/转发到 Vapor 而又可以直接使用 http://我的域名 来访问原有的页面烦请告知一下。
部署
修改运行端口
Vapor 4 对于指定运行端口和之前有了较大的区别。
在 main. swift 中做如下修改
app.http.server.configuration.hostname = "127.0.0.1" //响应的地址 0.0.0.0
app.http.server.configuration.port = 8000 //希望设定的端口
try configure(app)目前我没有找到如何在命令行下设置运行端口(Vapor 3 之前的方法好像已经不支持了)。如果有人了解烦请告知一下。
手动部署
由于我目前也还刚刚开始接触 Vapor,为了调试方便,我在本机的 Xcode 下进行开发。通过 github 作为中转,将本地的修改 commit 到 repository 上。手动在服务器端 fetch 并执行。在终端中执行的话当前终端将被任务锁定。
Docker 部署
另外,Vapor 的模板本身已经生成了 Dockerfile。也可以直接将完成后的项目生成 Docker Image。这种方式可以将项目发布到任何支持 Docker 的平台(mac、Linux、windows 等)。不过通常只适用于已经开发完善后的部署。更多细节可查阅 官方文档
Supervisior
Vapor Toolbox 已提供了对 Supervisior 的支持,可以很方便的通过 supervisor 来管理服务。
ubuntu 下安装 Supervisor
sudo apt-get update
sudo apt-get install supervisor我们需要为每一个项目创建一个 supervisor 配置文件。创建/etc/supervisor/conf. d/hello. conf
[program:hello]
command=/home/parallels/hello/.build/release/Run serve --env production
directory=/home/parallels/hello
user=parallels
stdout_logfile=/var/log/supervisor/%(program_name)-stdout.log
stderr_logfile=/var/log/supervisor/%(program_name)-stderr.log文件名是你的项目名。conf,目录指向你项目的根目录并设置好用户名
command=/home/parallels/hello/.build/release/Run serve --env production需确定已将项目编译成 release 版本,如果 vapor build 不好用,可以使用如下命令
cd ~/hello
swift build -c release通过 supervisor 管理项目
supervisorctl reread
supervisorctl add hello
supervisorctl start hello也可以通过 supervisor 的配置来指定运行端口
在/etc/supervisor/conf. d/hello. conf 中加入
environment=PORT=8123修改 main. swift
let port = Environment.get("PORT") ?? ""
app.http.server.configuration.port = Int(port) ?? 8080结语
希望本文能够对你开始使用 Vapor 4 带来一点帮助。同时也希望 Swift 能在更多的平台上有所表现。
Swift 已有更多的官方对 window 支持的迹象。