Project Perfect讓Swift在服務器端跑起來-讓Perfect更Rails (五)

編者語:努力會有回報,加油吧!html

       關於Perfect,已經從開發工具,原理,運行環境作了介紹。今天開始進入架構。其實,Perfect更像Java Servlet,我很喜歡Rails這種方式去構建。說句真心話,對於我這種.NET程序員,更但願只是換種語言,畢竟如今ASP.NET Core 很Cool。好!讓你們看看我對Perfect的改造。git

       再說說Perfect的運行原理。其實當用戶發送請求時,都是首先找到PerfectServerModuleInit()這個方法,根據指定規則去找對應的Handlers,以後經過Handlers的方法handleRequest去處理相對應的事務處理。咱們把這個流程用圖的方式表示一下。
       
       其實handleRequest很接近咱們的Controller,若是作成一個相似Rails的框架不是不可能的。這裏我參考了在Github上的一個項目(https://github.com/groovelab/SwiftBBS)你們也能夠去看看。程序員

       首先我要擴展一下PerfectLib中的WebRequest和WebResponse這兩個方法,針對WebRequest增長了action和參數,因爲用到Rails思想,因此action是不能缺乏的,後面的參數也是。而WebRepsonse把頁面渲染和數據JSON輸出作成統一的方法。這樣作的好處就是減小了每一個Handler一堆重複的工做.對應的文件是extension.Swiftgithub

[plain] view plain copyjson

 print?在CODE上查看代碼片派生到個人代碼片swift

  1. //  
  2. //  extension.swift  
  3. //  MVCDemo  
  4. //  
  5. //  Created by 盧建暉 on 16/2/27.  
  6. //  Copyright © 2016年 Kinfey. All rights reserved.  
  7. //  
  8.   
  9. import PerfectLib  
  10.   
  11. extension WebRequest {  
  12.     var action: String {  
  13.         return urlVariables["action"] ?? "index"  
  14.     }  
  15.     var acceptJson: Bool {  
  16.         return httpAccept().contains("application/json")  
  17.     }  
  18. }  
  19.   
  20. extension WebResponse {  
  21.     func render(templatePath: String, values: MustacheEvaluationContext.MapType) throws -> String {  
  22.         let fullPath =  templatePath  
  23.         let file = File(fullPath)  
  24.           
  25.         try file.openRead()  
  26.         defer { file.close() }  
  27.         let bytes = try file.readSomeBytes(file.size())  
  28.           
  29.         let parser = MustacheParser()  
  30.         let str = UTF8Encoding.encode(bytes)  
  31.         let template = try parser.parse(str)  
  32.           
  33.         let context = MustacheEvaluationContext(map: values)  
  34.         context.filePath = file.path()  
  35.         let collector = MustacheEvaluationOutputCollector()  
  36.         try template.evaluatePragmas(context, collector: collector, requireHandler: false)  
  37.         template.evaluate(context, collector: collector)  
  38.         return collector.asString()  
  39.     }  
  40.       
  41.     func renderHTML(templatePath: String, values: MustacheEvaluationContext.MapType) throws {  
  42.         let responsBody = try render(templatePath, values: values)  
  43.         appendBodyString(responsBody)  
  44.         addHeader("Content-type", value: "text/html")  
  45.     }  
  46.       
  47.     func outputJson(values: [String:JSONValue]) throws {  
  48.         addHeader("content-type", value: "application/json")  
  49.         let encoded = try values.jsonEncodedString()  
  50.         appendBodyString(encoded)  
  51.     }  
  52. }  

       接下來咱們作一個Controller.swift的基類,這個基類繼承自RequesHandler包括了每一個action所返回的結果。我這裏參照.NET Core 把返回結果封裝成IActionResult.並把handlerRequest作成一個統一處理的方法。架構

[plain] view plain copyapp

 print?在CODE上查看代碼片派生到個人代碼片框架

  1. //  
  2. //  Controller.swift  
  3. //  MVCDemo  
  4. //  
  5. //  Created by 盧建暉 on 16/2/26.  
  6. //  Copyright © 2016年 Kinfey. All rights reserved.  
  7. //  
  8.   
  9. import PerfectLib  
  10.   
  11. class Controller : RequestHandler{  
  12.       
  13.       
  14.     enum IActionResult {  
  15.         case View(templatePath: String?, values: [String: Any])  
  16.         case Redirect(url: String)  
  17.         case Error(status: Int, message: String)  
  18.     }  
  19.       
  20.       
  21.     var request: WebRequest!  
  22.     var response: WebResponse!  
  23.       
  24.       
  25.     func handleRequest(request: WebRequest, response: WebResponse) {  
  26.         self.request = request  
  27.         self.response = response  
  28.           
  29.           
  30.           
  31.         defer {  
  32.             response.requestCompletedCallback()  
  33.         }  
  34.           
  35.         do{  
  36.           
  37.         switch try Action(request.action) {  
  38.         case let .View(templatePath, responseValues):  
  39.             let values = responseValues  
  40.             if request.acceptJson {  
  41.                 try response.outputJson(values)  
  42.             } else if let templatePath = templatePath {  
  43.                 try response.renderHTML(templatePath, values: values)  
  44.             }  
  45.         case let .Redirect(url):  
  46.             response.redirectTo(url)  
  47.         case let .Error(status, message):  
  48.             response.setStatus(status, message: message)  
  49.             break;  
  50.         }  
  51.         }catch let e {  
  52.             print(e)  
  53.         }  
  54.     }  
  55.       
  56.     func Action(action: String) throws -> IActionResult {  
  57.         return .Error(status: 500, message: "need implement")  
  58.     }  
  59.       
  60. }  

       爲什麼要這樣作,這裏有一個方法Action,根據URL Routing去找到對應的Action方法,以後經過handlerRequest處理返回對應的頁面或者JSON數據,咱們作一個HomeController.swift看看。ide

[plain] view plain copy

 print?在CODE上查看代碼片派生到個人代碼片

  1. import PerfectLib  
  2.   
  3. class HomeController: Controller {  
  4.       
  5.     override func Action(action: String) throws -> IActionResult {  
  6.           
  7.         switch request.action {  
  8.         case "about" :  
  9.             return try About()  
  10.         default:  
  11.             return try Index()  
  12.         }  
  13.           
  14.     }  
  15.       
  16.     func Index() throws -> IActionResult{  
  17.           
  18.         var values = [String: Any]()  
  19.           
  20.         values["str"]="Hello Swift MVC Framework"  
  21.           
  22.         return .View(templatePath: "Index.mustache", values: values)  
  23.     }  
  24.       
  25.     func About() throws -> IActionResult{  
  26.           
  27.         var values = [String: Any]()  
  28.           
  29.         values["str"]="Hello Swift MVC Framework"  
  30.           
  31.         return .View(templatePath: "About.mustache", values: values)  
  32.     }  
  33.       
  34.       
  35. }  

      這裏就是咱們改造後的HomeController,而對應的URL Routing我參照.NET Core的方式放在Startup.swift上

[plain] view plain copy

 print?在CODE上查看代碼片派生到個人代碼片

  1. import PerfectLib  
  2.   
  3. public func PerfectServerModuleInit() {  
  4.       
  5.     Routing.Handler.registerGlobally();  
  6.     Routing.Routes["GET", ["/","/Home/{action}"] ] = { _ in return HomeController() }  
  7.       
  8.       
  9. }  

      最後咱們把頁面加上 index.mustache

[html] view plain copy

 print?在CODE上查看代碼片派生到個人代碼片

  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4.     <title>Swift MVC</title>  
  5. </head>  
  6. <body>  
  7. <h1>{{str}}</h1>  
  8. </body>  
  9. </html>  

 

     about.mustache

[html] view plain copy

 print?在CODE上查看代碼片派生到個人代碼片

  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4. <title>About</title>  
  5. </head>  
  6. <body>  
  7. <h1>This is Kinfey design</h1>  
  8. </body>  
  9. </html>  

    基本上就能夠完成咱們的Rails改造了,看看在Xcode的結構,很Rails,很.NET Core吧
    
    看看運行的過程,如圖
    
    咱們運行下
       

    
    這裏補充一點,若是你要把頁面模版在Xcode中使用必需要對Build Phase進行設置,在Copy Files中添加,須要設置Desination爲Product Directory,如圖
          今天說到這裏,祝週末愉快!

相關文章
相關標籤/搜索