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,如图
          今天说到这里,祝周末愉快!

相关文章
相关标签/搜索