Perfect-SwiftのRESTサヌバヌ

画像
ほずんどのiOS開発者は遅かれ早かれiOS SDKの䞖界でcr屈になりたす。 そしお、その理由は、IOSが深刻な開発の機䌚を十分に持っおいないずいうこずではありたせんが、最新の深刻なアプリケヌションにはクラむアントサヌバヌアヌキテクチャがありたすが、iOS開発者が利甚できるのはClient Worldだけです。 サヌバヌ開発は、モバむル開発者がAPIの䜕かを倉曎するずいう芁件に非垞にjeしおいるプログラマにずっお 、サヌバヌ鹿に翻匄されたす。 最も単玔なAPIを実装するには、独自のパラダむムずニュアンスを備えた別のプログラミング蚀語を習埗する必芁があるずいう事実に楜芳䞻矩を加えるこずはありたせん。 パむロットのアむデアを打ち砎るには、倖郚から人々を匕き付けるか、゚むリアンの倢の䞖界PHP、Pyton、Ruby、Cに突入する必芁がありたす。 Objectove-C / Swiftキャンプのすべおがそれほど悪いですか たったく違いたす。 サヌバヌ開発のテヌマに぀いお少し調べおみるず、かなり興味深い取り組みに出くわしたした。これは、真のクロスプラットフォヌム゜リュヌションになるず䞻匵しおいたす。぀たり、OSXず* nixシステムの䞡方で同じように機胜したすWindows、C 誰も動かない-あたりにも倚くのグッズ。

プロゞェクトの䜜成者が䞻匵するずおり、パヌフェクト-Swiftプログラミング蚀語を䜿甚しおアプリケヌションやその他のRESTサヌビスを䜜成する開発者にずっお理想的なWebサヌバヌおよびツヌルキットです。 「理想」はしゃれにすぎないこずは明らかですが、同時に、提案された解決策に粟通した埌、この声明に少し真実があるずいう事実に傟き始めたす。

「プレス」は、新しいプログラミング蚀語のアプロヌチに関する蚘事を掲茉したした。これは、アップルによる簡単な提出により業界暙準になる可胜性がありたす。 倧衆に昇栌したSwiftに基づく蚀語。 原則ずしお、これに関する蚘事はより倚くの質問を匕き起こし、すべおを再トレヌニングするのにうんざりしおいる人たちにずっおはさらに迷惑ですSwift自䜓は非垞に急速に倉化したす。 しかし、問題の研究を掘り䞋げお、すべおが芋かけよりもはるかに優れおいるこずが明らかになりたした。

Perfectは、新しいサヌバヌ開発蚀語ではありたせん。 Perfectは、最新の実装Swift 2.2の執筆時点のSwiftのみを䜿甚しおREST APIサヌビスを䜜成できるサヌバヌ環境です。クラむアント開発者が毎日しなければならないこずを超えるものはありたせん。


するこず名刺ペヌゞスタブを䜜成しお、サヌバヌにアクセスするずきにそれを実蚌したす。 GET / POSTリク゚ストに応答するREST APIサヌビスを簡単に䜜成する可胜性を瀺したす。 静的なサむトペヌゞの動的な圢成のメカニズムを瀺したす。 さらに、これはすべおSwiftで行いたす。

モノ
Monoの愛奜家は、クロスプラットフォヌムのサヌバヌ開発ツヌルが長い間存圚しおいるず䞻匵するこずができ、C/のサヌバヌ偎に粟通しおいる人にずっお重芁な「BUT」があるず異議を唱えるこずは困難です。嫌悪感を匕き起こしたす。


免責事項
HATEOASずHALの䌝道者は通り過ぎるこずができたす。 モバむル開発者が慣れおいる圢でRESTに぀いお蚀及したす。 そのようなRESTの玔床は疑問芖される可胜性がありたすが、これからはなくなるこずはありたせん。 アフリカゟりをゟりず芋なすこずができるか、むンドゟりのみをゟりず芋なすこずができるかに぀いお、ホリバヌを手配する必芁はありたせん。


ですから、旅の出発点は適切な環境の創造です。 残念ながら、歩き続ける道はそれほど明癜ではなく、ワヌクスペヌスの䜜成からXcodeでのスキヌムの倉曎たで、非垞に奇劙な操䜜が数倚く含たれたす。 プロゞェクトの䜜成者が䜜成したビデオに、ステップバむステップのガむドが瀺されおいたす。 蚘事の各ステップを説明するこずは、ハブにずっおの「翻蚳」ずいうトピックにすぎたせん。 パヌフェクト機胜の実甚化に焊点を圓おたいず思いたす。パヌフェクト機胜は、コマヌシャルには存圚しないか、 巚倧な神秘的な圢でそこに衚瀺されたす。 ずころで、著者によっお公開されたビデオの䞭には、有甚ずいうよりも有害なものがありたす。



はじめに、抂念を理解したしょう。 Perfectは、サヌバヌラむブラリPerfectLibずミニマリストむンタヌフェむスを備えたスタヌトアップアプリケヌションPerfect Serverの2぀の郚分で構成されおいたす。 どちらのアプリケヌションもオヌプン゜ヌスであり、理論的には、自分でニヌズを倉曎/終了できたす。 ただし、これを行うこずは匷くお勧めしたせん。 個人的に、私は垞に䜕かを改善するために忍び寄っおいたす。 ただし、PerfectはSwift 3での䜿甚に適応しおいないこずに泚意しおください。蚀語の䜜成者は、Swift 3が「トップツヌボトム」サポヌトを持たないず蚀いたす。 Swiftの新しいバヌゞョンにアップグレヌドするために、すでに行われた倉曎を完党に取り陀く必芁がありたす。

「Hello Perfect」の段階に到達しおいない堎合は、それを実行する時間です 。必芁な環境をここからダりンロヌドできたす。 プロゞェクトサむトのリンクの䞀郚が壊れおいたす

次に、index.htmlおよびtemplate.htmlファむルを䜜成し、䜜業プロゞェクトに远加したす。 远加した埌、ビルドフェヌズに進み、「新しいファむルコピヌフェヌズ」ステップを远加したしょう。

画像

最終的に、りィンドりは次のようになりたす。

画像

゜ヌスパッケヌゞindex.htmlのダりンロヌド
<HTML> <HEAD> <TITLE>My company name.</TITLE> </HEAD> <BODY BGCOLOR="FFFFFF"> <CENTER><IMG SRC="http://www.telemis.com/sites/default/files/Cloud-Plain-Blue_2.png" width="300px"> </CENTER> <HR> <a href="http://demonsoft.net">My name</a> is a link to another nifty site <H1>This is a Header</H1> <H2>This is a Medium Header</H2> Send me mail at <a href="mailto:support@yourcompany.com"> mail@gmail.com</a>. <P> This is a new paragraph! <P> <B>This is a new paragraph!</B> <B><I>This is a new sentence without a paragraph break, in bold italics.</I></B> <HR> </BODY> </HTML> 




゜ヌスTemplate.htmlをダりンロヌド
 <HTML> <HEAD> <TITLE>{{TITLE}}</TITLE> </HEAD> <BODY> <table border = "1px" width="100%"> <tr> <th>{{TITLE}}</th> </tr> </table> <table border = "1px" width="100%"> <tr> <th align="left">{{BODY}}</th> </tr> </table> <table border = "0px" width="100%" > <tr> <th align="right">{{FOOTER}}</th> </tr> </table> </BODY> </HTML> 



ほずんどの堎合、これらすべおのアクションはプロゞェクトペヌゞのビデオで説明されおいたす www.youtube.com/watch?v=J441eJ40PH4ただし、このケヌスでは、Webペヌゞをホストするか、REST APIを䜿甚できたす。 私たちは䞡方のニヌズを1぀の機䌚に結合しようずしたす。

PerfectServerModuleInitコヌドを次のコヌドに完党に眮き換えたす。
PerfectServerModuleInit
public func PerfectServerModuleInit()
{
Routing.Handler.registerGlobally()

// Root index.html page
Routing.Routes["*"] = { (_:WebResponse) in StaticFileHandler() }

// Request for static pages
Routing.Routes["GET", ["/index", "/list"]] = { (_:WebResponse) in return StaticPageHandler(staticPage: "index.html") }

// REST
Routing.Routes["GET", ["/hello"]] = { (_:WebResponse) in return HelloHandler() }
Routing.Routes["GET", ["/help"]] = { (_:WebResponse) in return HelpHandler() }
Routing.Routes["GET", ["/cars", "/car"]] = { (_:WebResponse) in return CarsJson() }


Routing.Routes["POST", ["/list"]] = { (_:WebResponse) in return CarsJson() }
}


PerfectServerModuleInitメ゜ッドの呌び出しはサヌバヌプロゞェクト私の堎合はMySwiftServerにありたすが、どのクラスにも関連付けられおいたせん。 別の.swiftファむルに入れたした。
PerfectServerModuleInitは、Webサヌビスぞの゚ントリポむントです。 main関数に䌌おいたす。 サヌバヌラむブラリから呌び出されるメ゜ッド。 埌でここで䜕が起こっおいるのかを説明したす。

ここで、さらにいく぀かのクラスを远加する必芁がありたす。

クラスhellohandler
import Foundation
import PerfectLib

class HelloHandler:RequestHandler
{
func handleRequest(request: WebRequest, response: WebResponse)
{
response.appendBodyString("Hello World!\n")
response.appendBodyString("Hello Perfect!\n")
response.appendBodyString("Hello Swift Server!\n")
response.requestCompletedCallback()
}
}


HelloHandlerクラスは圹に立たないため、䞻にサヌバヌが皌働しおいるこずを確認するために䜿甚されたす。 サヌバヌの応答は、出力バッファヌに行を远加し、クラむアントブラりザヌたたはクラむアントアプリケヌションにコヌルバックを远加するこずになりたす。

クラスStaticPageHandler
import Foundation
import PerfectLib

class StaticPageHandler:RequestHandler
{
var staticPage = "index.html"

internal init(staticPage:String) {
self.staticPage = staticPage
}

func handleRequest(request: WebRequest, response: WebResponse)
{
let file = ContentPage().page(request.documentRoot, pageFile: self.staticPage)

response.appendBodyString(file)
response.requestCompletedCallback()
}
}


StaticPageHandlerクラスを䜿甚するず、指定した名前で静的ペヌゞをホストできたす。 「デフォルトで」index.htmlが䜿甚されたすが、原則ずしお、プロゞェクトに远加された他のペヌゞでもかたいたせん。

HelpHandlerクラス
import Foundation
import PerfectLib

class HelpHandler:RequestHandler
{
func handleRequest(request: WebRequest, response: WebResponse)
{
let list = Routing.Routes.description.stringByReplacingString("+h", withString: "")
let html = ContentPage(title:"HELP", body:list).page(request.documentRoot)
response.appendBodyString("\(html)")
response.requestCompletedCallback()
}
}


HelpHandlerクラスを䜿甚するず、サヌバヌによっお凊理されたコマンドのリストを取埗できたす。 他のサヌバヌ環境MS Framework 4.5.1などでは、自己文曞化REST APIサヌバヌを取埗できたす。 これは、モバむルアプリケヌションの開発者にずっお非垞に䟿利です。サヌバヌの開発者にメンテナンスやコマンドの远加を䟝頌する必芁はありたせん。
曎新 次の蚘事では、APIドキュメントのメカニズムを改善したした。

クラスCarsJson
import Foundation
import PerfectLib

class CarsJson:RequestHandler
{
func handleRequest(request: WebRequest, response: WebResponse)
{
let car1:[JSONKey: AnyObject] = ["Wheel":4, "Color":"Black"]
let car2:[JSONKey: AnyObject] = ["Wheel":3, "Color":["mixColor":0xf2f2f2]]
let cars = [car1, car2]
let restResponse = RESTResponse(data:cars)
response.appendBodyBytes(restResponse.array)
response.requestCompletedCallback()
}
}


CarsJsonクラスは、GET / POST芁求が耇雑な戻り構造でどのように機胜するかを瀺しおいたす。 返されるデヌタはAnyObjectによっお衚されたす。

クラスContentPage
import Foundation

public class ContentPage:NSObject
{
private var title = ""
private var body = ""
private var footer = ""

public init(title:String="", body:String="", footer:String="Copyright (C) 2016 _MY_COMPANY_NAME_. All rights reserved.")
{
self.title = title
self.body = body
self.footer = footer
}

func page(webRoot:String, pageFile:String="template.html") -> String
{
let template = self.loadIndexHtml(webRoot, pageFile:pageFile)
let htmlBody = self.body.stringByReplacingString("\n", withString: "
")

var page = template
page = page.stringByReplacingString("{{TITLE}}", withString: self.title)
page = page.stringByReplacingString("{{BODY}}", withString: htmlBody)
page = page.stringByReplacingString("{{FOOTER}}", withString: self.footer)

return page
}

private func loadIndexHtml(webRoot:String, pageFile:String) -> String {

let fileManager = NSFileManager.defaultManager()
let directory = fileManager.currentDirectoryPath
let path = directory + "/\(webRoot)/\(pageFile)"
do {
return try String(contentsOfFile: path)
} catch {
print("File didn't create")
}

return "404 NOT FOUND"
}
}


静的なペヌゞを返し、察応するフィヌルドで眮換を行うサヌビスクラス。 デフォルトでは、template.htmlテンプレヌトが䜿甚されたすが、原則ずしお、プロゞェクトに远加された他のテンプレヌトを䜿甚できたす。

クラスRESTResponse
import Foundation
import PerfectLib

public class RESTResponse
{

public var data:AnyObject = "" // Data from Server to Client
public var status = "" // HTTP status or other code of operation.
public var message = "" // This message Client can show in the Alert View
public var errors = [String]() // All real errors for Console


public init(data:AnyObject="", status:String="200", message:String="", errors:[String]=[])
{
self.data = data
self.status = status
self.message = message
self.errors = errors
}

public var array: [UInt8] {

let result = ["data":self.data, "status":self.status, "message":self.message, "errors":self.errors]
return serialization(result)
}

private func serialization(object:AnyObject) -> [UInt8]
{
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(object, options: NSJSONWritingOptions.PrettyPrinted)
let count = jsonData.length / sizeof(UInt8)
var jsonArray = [UInt8](count: count, repeatedValue: 0)
jsonData.getBytes(&jsonArray, length:count * sizeof(UInt8))
return jsonArray

} catch let error as NSError {
print(error)
}

return [UInt8]()
}
}


RESTResponseクラスは、REST APIを䜿甚する重芁なクラスであり、最も物議をかもしおいたす。
返されるデヌタオブゞェクトには、次のJSONラッパヌ構造がありたす。
{
«data»:{
. },
«errors»:[
 ],
"message":"",
"status":200
}


1䞀郚の開発者は、この圢匏を明確に受け入れたせん。 しかし、返されたデヌタのこの圢匏に察する説埗力のある議論はありたせんでした。 ただし、型付き圢匏には特定の利点がありたす。 歎史的な䌝統によれば、「ステヌタス」フィヌルドの倀は、操䜜が正垞に完了するず「200」になりたす。 ただし、HTTP応答ずは盎接の関係はありたせん。 これらは垞にリク゚ストヘッダヌから差し匕くこずができ、サヌバヌ環境自䜓によっおクラむアント偎に送信されたす。 「ステヌタス」フィヌルドでは、HTTP局ではなく、Webサヌビスのビゞネスロゞック内でセマンティックロヌドを持぀倀を枡すこずができたす。 「メッセヌゞ」フィヌルドには、サヌバヌ偎のアクションを通知するためにナヌザヌに衚瀺する必芁がある文字列が含たれおいたす。 たずえば、特にクラむアントアプリケヌションの芳点からすべおが順調に進んでいる堎合のセッションの有効期限に関する情報。 「゚ラヌ」[]フィヌルドは、異垞な状況をクラむアントに通知する甚語の配列です。 この情報をクラむアント偎で保存しお凊理したり、さらに凊理するために専甚のログサヌバヌに送信したりするず䟿利です。 最埌に、デヌタフィヌルド{.... }には耇雑なデヌタ構造が含たれおいたす-クラむアント偎で受信されるこずが正確に予想されるものです。 このアプロヌチの䞻な利点は、サヌバヌの応答が指定されたスキヌムを満たさない堎合、クラむアントが明確な良心を持っお拒吊できるこずです。 これを敎理する方法に぀いおは、 habrahabr.ru / post / 283012を参照しおください。説明したアプロヌチに反察する開発者は、「配列」プロパティを簡単に倉曎しお、説明したラッパヌなしで生デヌタを返すこずができたす。

2Perfect開発者がJSONオブゞェクトを返すために䜿甚するこずを提案したアプロヌチに少々驚いた。 提案されたチュヌトリアルでは、各JSONオブゞェクトは、[JSONKeyJSONValue][StringAnyObject]ず同等の圢匏のキヌ/倀のペアを列挙しおツリヌを順次構築するこずにより圢成されるず想定されおいたした。 次のコヌドを䜿甚するこずになっおいたす。

public var json: String
{
let result = ["data":self.data, "status":self.status, "message":self.message, "errors":self.errors]

let jsonEncoder = JSONEncoder()
do {
let respString = try jsonEncoder.encode(result)
return respString
} catch let error as NSError {
print(error)
}
return ""
}

..
response.appendBodyString(self.json)
response.requestCompletedCallback()


ただし、このコヌドは、珟圚CarsJsonクラスに含たれおいるデヌタでも完党に動䜜したせん。 さらに、Perfect creatorsチュヌトリアルのロゞックに埓う堎合、耇雑な構造を゚ンコヌドするには、返されるオブゞェクトごずに独自のクラスを䜜成する必芁がありたす。 私のバヌゞョンでは、数行のコヌドで任意のネストのオブゞェクトをシリアル化できるこずは明らかです。 これは、サヌビスの開始埌に確認するこずは難しくありたせん。

発射

すべおが正しく行われるず、ブラりザに次のペヌゞが衚瀺されたす。
画像

サヌバヌに挚拶するブラりザの行に「/ hello」ず入力したす
画像

実行されるコマンドに関するサヌバヌ情報を芁求したす。
画像

すべおのコマンドはカテゎリGET、POSTに分かれおいるこずに泚意しおください。 他の述語を䜿甚する堎合、それらもこのリストに衚瀺されたす。
ただし、/ listコマンドは䞡方のリストにもありたす。 PerfectServerModuleInitによっおGETおよびPOSTコマンドに远加されたのは圌女ですが、異なるハンドラヌがありたす
ブラりザのコマンドラむンからリストにアクセスするず、むンデックスペヌゞが衚瀺されたす。
画像

しかし、特別なアプリケヌションたずえば、無料のPostmanを䜿甚する堎合、Postリク゚ストの本文には、予想されるJSONが含たれたす。
画像

JSONはCarsJsonクラスで生成された構造ず完党に䞀貫しおいるこずに泚意しおください。

しかし、GETから/​​ listコマンドを削陀するずどうなりたすか
Routing.Routes["GET", ["/index", "/list"]] = { (_:WebResponse) in return StaticPageHandler(staticPage: "index.html") }

取埗するもの
画像

このような簡単な方法で、開始ペヌゞたたはスタブペヌゞを眮き換えるこずで、RESTコマンドの䜿甚をルヌルずしお簡単にマスクできたす。これが、オヌプンAPIを䜿甚したサヌビスでない堎合、/ helpなどの開発コマンドの最埌に削陀たたはブロックされたす。 しかし、この機䌚を別の方法で掻甚するこずもできたす。GET芁求のコマンドに関する詳现を含む情報を衚瀺し、静的ペヌゞハンドラヌを機胜させたす。

/ carおよび/ carsコマンドは同じ構造を返すが、Webペヌゞずしお返されるこずが予想されたす。
画像

ある皋床のスキルがあれば、REST APIを䜜成するこずは、必芁なナビゲヌションを備えたViewControllerを䜜成するこずほど難しくありたせん。

゚クストリヌムスポヌツのファンは、これらすべおをLinuxで実行しようずするこずができたす。

曎新
チュヌトリアルでJSON応答の神秘的なシリアル化が䜿甚された理由に぀いおの説明がありたした-すべおのNSメ゜ッドNSJSONSerializationを含むはUbuntuで動䜜を停止したす。

Source: https://habr.com/ru/post/J283260/


All Articles