Takahiro Octopress Blog

-1から始める情弱プログラミング

Server Side Swift: Perfect を使ってみよう!

Server Side SwiftライブラリのPerfect

本日は以前書いたサーバサイドSwiftの続きを書きます!
と言いたかったところなのですが、Swift ExpressはSwift3.0やXcode8に対応しておらず、何もできなかったため、方向転換して最もSTAR数の多いPerfectlySoft/Perfectを使うことにしました。

よくよく見るとMySQLだけでなくMongoDB接続用にもモジュールが用意されており、なかなか良さそうではないですか!!
とは言いつつも、そんなにすぐにMaster Of Perfectにはなれないので少しずつ見ていくことにします。

チュートリアルを触ってみる

まずは何はともあれGitHubの Getting Started からやらないと話になりません。
手順は簡単です。

1.テンプレートプロジェクトをクローンする
git clone https://github.com/PerfectlySoft/PerfectTemplate.git

2.ビルドを実行する
クローンしたPerfectTemplateフォルダ内に入り、ビルドを実行しましょう。

1
2
$ cd PerfectTemplate
$ swift build

3.サーバを起動します
なんと後は下記コマンドでサーバを起動するだけです。

.build/debug/PerfectTemplate

正しく起動すれば、下記ログが出力されます。
Starting HTTP server on 0.0.0.0:8181 with document root ./webroot
またログの指示通りChromeでhttp://localhost:8181/にアクセスすればHello Worldが拝めます。

ルーティングの書き方について学ぶ

では次に簡単なルーティングについて学んでいきましょう。
チュートリアルでは、下記GETリクエストのみ受け付けていました。

1
2
3
4
5
6
7
8
// main.swift
var routes = Routes()
routes.add(method: .get, uri: "/", handler: {
  request, response in
  response.setHeader(.contentType, value: "text/html")
  response.appendBody(string: "<html><title>Hello, world!</title><body>Hello, world!</body></html>")
  response.completed()
})

最も単純なGETリクエストですね。
では、ID: 100のユーザ情報を取得するGETリクエストはどう受け付けるのでしょうか。

1
2
3
4
5
6
7
8
// main.swift
var routes = Routes()
routes.add(method: .get, uri: "/user/{id}", handler: {
  request, response in
  response.setHeader(.contentType, value: "text/html")
  response.appendBody(string: "<html><title>Sample</title><body>You GET UserInfo with \(request.urlVariables)</body></html>")
  response.completed()
})

たったのこれだけです。
では、POSTリクエストの受け付けはどうでしょうか。

1
2
3
4
5
6
7
// main.swift
var routes = Routes()
routes.add(method: .post, uri: "/user", handler: {
  request, response in
  response.appendBody(string: "<html><title>Sample</title><body>You POSTed user data to catch your POST request.</body></html>")
  response.completed()
})

これも簡単ですね。
書き方に若干の違いはあれど、最早Node.jsとそんなに変わらん…

おまけで、POSTリクエストで届いたJSONStringをバラバラっと分解して返却してみました。
そのためにまずはJSONStringをデコードする処理を実装します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// decode.swift
import PerfectHTTPServer
import PerfectLib

func decode(postBody: String?) -> [String: Any]? {
  do {
      guard let decoded = try postBody?.jsonDecode() as? [String:Any] else {
          return [:]
      }
      print(decoded)
      return decoded
  } catch {
      return [:]
  }
}

これを下記のように利用します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// main.swift
var routes = Routes()
routes.add(method: .post, uri: "/user", handler: {
  request, response in

  var userInfo = ""
  let decodedParam = decode(postBody: request.postBodyString)
  for (key, value) in decodedParam! {
      switch key {
          case "name":
              userInfo = userInfo + "name is \(value as! String).\n"
          case "email":
              userInfo = userInfo + "email is \(value as! String)."
          default:
              break
      }
  }
  response.appendBody(string: "<html><body>POST handler: \(userInfo)</body></html>")
  response.completed()
})

さて、実装できたのでクライアントからリクエストを投げてみます。

1
2
3
4
5
// クライアントからPOSTリクエストを投げます
curl http://localhost:8181/user -X POST -H "Content-Type: application/json" -d '{"name":"Ichiro", "email": "xxx@gmail.com"}'
// 結果
<html><body>POST handler: name is Ichiro.
email is xxx@gmail.com.</body></html>xxxx:PerfectTemplate

まとめ

さて今回はPerfectを使ったサーバサイドSwiftを見てみました。
まだまだ基本的なリクエストの受付しかみていませんが、既にいろいろなモジュールが用意されているようなので、継続的に見ていきたいと思います。
やっぱりライブラリを作るなら、最新の状況についていかないと見捨てられるな〜と思ってしまいました。
(今回で言うと、Swift3やXcode8とかですね。)

Comments