编者语:努力会有回报,加油吧!
关于Perfect,已经从开发工具,原理,运行环境做了介绍。今天开始进入架构。其实,Perfect更像JavaServlet,我很喜欢Rails这种方式去构建。说句真心话,对于我这种.NET程序员,更希望只是换种语言,毕竟现在ASP.NET Core 很Cool。好!让大家看看我对Perfect的改造。
再说说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.Swift
[plain]view plaincopy
- //
- //extension.swift
- //MVCDemo
- //
- //Createdby卢建晖on16/2/27.
- //Copyright©2016年Kinfey.Allrightsreserved.
- //
- importPerfectLib
- extensionWebRequest{
- varaction:String{
- returnurlVariables["action"]??"index"
- }
- varacceptJson:Bool{
- returnhttpAccept().contains("application/json")
- }
- }
- extensionWebResponse{
- funcrender(templatePath:String,values:MustacheEvaluationContext.MapType)throws->String{
- letfullPath=templatePath
- letfile=File(fullPath)
- tryfile.openRead()
- defer{file.close()}
- letbytes=tryfile.readSomeBytes(file.size())
- letparser=MustacheParser()
- letstr=UTF8Encoding.encode(bytes)
- lettemplate=tryparser.parse(str)
- letcontext=MustacheEvaluationContext(map:values)
- context.filePath=file.path()
- letcollector=MustacheEvaluationOutputCollector()
- trytemplate.evaluatePragmas(context,collector:collector,requireHandler:false)
- template.evaluate(context,collector:collector)
- returncollector.asString()
- }
- funcrenderHTML(templatePath:String,values:MustacheEvaluationContext.MapType)throws{
- letresponsBody=tryrender(templatePath,values:values)
- appendBodyString(responsBody)
- addHeader("Content-type",value:"text/html")
- }
- funcoutputJson(values:[String:JSONValue])throws{
- addHeader("content-type",value:"application/json")
- letencoded=tryvalues.jsonEncodedString()
- appendBodyString(encoded)
- }
- }
接下来我们做一个Controller.swift的基类,这个基类继承自RequesHandler包括了每个action所返回的结果。我这里参照.NET Core 把返回结果封装成IActionResult.并把handlerRequest做成一个统一处理的方法。
[plain]view plaincopy
- //
- //Controller.swift
- //MVCDemo
- //
- //Createdby卢建晖on16/2/26.
- //Copyright©2016年Kinfey.Allrightsreserved.
- //
- importPerfectLib
- classController:RequestHandler{
- enumIActionResult{
- caseView(templatePath:String?,values:[String:Any])
- caseRedirect(url:String)
- caseError(status:Int,message:String)
- }
- varrequest:WebRequest!
- varresponse:WebResponse!
- funchandleRequest(request:WebRequest,response:WebResponse){
- self.request=request
- self.response=response
- defer{
- response.requestCompletedCallback()
- }
- do{
- switchtryAction(request.action){
- caselet.View(templatePath,responseValues):
- letvalues=responseValues
- ifrequest.acceptJson{
- tryresponse.outputJson(values)
- }elseiflettemplatePath=templatePath{
- tryresponse.renderHTML(templatePath,values:values)
- }
- caselet.Redirect(url):
- response.redirectTo(url)
- caselet.Error(status,message):
- response.setStatus(status,message:message)
- break;
- }
- }catchlete{
- print(e)
- }
- }
- funcAction(action:String)throws->IActionResult{
- return.Error(status:500,message:"needimplement")
- }
- }
为何要这样做,这里有一个方法Action,根据URL Routing去找到对应的Action方法,之后通过handlerRequest处理返回对应的页面或者JSON数据,我们做一个HomeController.swift看看。
[plain]view plaincopy
- importPerfectLib
- classHomeController:Controller{
- overridefuncAction(action:String)throws->IActionResult{
- switchrequest.action{
- case"about":
- returntryAbout()
- default:
- returntryIndex()
- }
- }
- funcIndex()throws->IActionResult{
- varvalues=[String:Any]()
- values["str"]="HelloSwiftMVCFramework"
- return.View(templatePath:"Index.mustache",values:values)
- }
- funcAbout()throws->IActionResult{
- varvalues=[String:Any]()
- values["str"]="HelloSwiftMVCFramework"
- return.View(templatePath:"About.mustache",values:values)
- }
- }
这里就是我们改造后的HomeController,而对应的URL Routing我参照.NET Core的方式放在Startup.swift上
[plain]view plaincopy
- importPerfectLib
- publicfuncPerfectServerModuleInit(){
- Routing.Handler.registerGlobally();
- Routing.Routes["GET",["/","/Home/{action}"]]={_inreturnHomeController()}
- }
[html]view plaincopy
- <!DOCTYPEhtml>
- <htmllang="en">
- <head>
- <title>SwiftMVC</title>
- </head>
- <body>
- <h1>{{str}}</h1>
- </body>
- </html>
about.mustache
[html]view plaincopy
- <!DOCTYPEhtml>
- <htmllang="en">
- <head>
- <title>About</title>
- </head>
- <body>
- <h1>ThisisKinfeydesign</h1>
- </body>
- </html>
基本上就可以完成我们的Rails改造了,看看在Xcode的结构,很Rails,很.NET Core吧
看看运行的过程,如图
我们运行下
这里补充一点,如果你要把页面模版在Xcode中使用必须要对Build Phase进行设置,在Copy Files中添加,需要设置Desination为Product Directory,如图
今天说到这里,祝周末愉快!