您的位置:新葡亰496net > 奥门新萄京娱乐场 > 0服务端开发,Mustache页面模板与日志记录

0服务端开发,Mustache页面模板与日志记录

发布时间:2019-07-05 12:56编辑:奥门新萄京娱乐场浏览(76)

    今天博客中就来聊一下Perfect框架的静态文件的添加与访问,路由的配置以及表单的提交。虽然官网上有聊静态文件的访问的部分,但是在使用Perfect框架来访问静态文件时还是有些点需要注意的,这些关键点在其官方文档上并未提出。今天我们要做的事情就是通过浏览器访问静态文件,然后在静态文件中使用form表单往指定的路由上进行提交相应的数据。

    入门篇主要讲述Swift语法,转行篇主要讲述用Swift编写服务器程序。

    本篇博客主要介绍如果在Perfect工程中引入和使用Mustache页面模板与日志记录系统。Mustache页面模板类似于PHP中的smarty模板引擎或者Java中的JSTL标签。当然Mustache页面模板的功能要弱的多。Mustache页面模板的主要功能是将html页面中的标签变量(比如“{{name}}”)进行替换,要想引入Mustache页面模板相关的库,只需要在Package.swift文件中添加相应的库的连接地址然后再编译连接即可。本篇博客还会介绍如果将日志记录到相应的日志文件,在开发中日志是不可或缺的,所以日志系统的引入与配置也是必不可少的。

    本篇博客主要介绍如果在Perfect工程中引入和使用Mustache页面模板与日志记录系统。Mustache页面模板类似于PHP中的smarty模板引擎或者Java中的JSTL标签。当然Mustache页面模板的功能要弱的多。Mustache页面模板的主要功能是将html页面中的标签变量(比如“{{name}}”)进行替换,要想引入Mustache页面模板相关的库,只需要在Package.swift文件中添加相应的库的连接地址然后再编译连接即可。本篇博客还会介绍如果将日志记录到相应的日志文件,在开发中日志是不可或缺的,所以日志系统的引入与配置也是必不可少的。

     

    入门篇

    Swift语法的设计原则:简洁、安全、现代化。

    基于简洁的原则:每条语句后的分号可以不用写了(写上分号也没错),OC数据类型字面量语法中的@符号去了,import语句简洁到令人窒息。

    基于安全原则:字典的value为nil不会崩溃。

    基于现代化原则:加入了泛型等。

    下方会对Mustache页面模板与日志系统的配置进行介绍。

    下方会对Mustache页面模板与日志系统的配置进行介绍。

    一、静态文件的添加与访问

    1、打印:

    还能想到比print更合适的方式么

    print("Hello, world!")
    

     

     

    1、未使用Xcode管理的Perfect的静态文件根目录的配置

    2、常量(constant)和变量(variable)

    声明常量用let,变量用var。

    var myVariable = 42
    let myConstant = 42
    let explicitDouble: Double = 70
    

    可以在声明一个变量或常量的时候提供类型标注,来明确变量或常量能够储存值的类型。添加类型标注的方法是在变量或常量的名字后边加一个冒号,再跟上要使用的类型名称。

    可变数据类型(如数组、字典等)用var,不可变数据类型用let。

    一、Mustache页面模板

    一、Mustache页面模板

    在PHP开发或者Java Web开发中,都有一个根目录来存储相应的静态文件,比如wwwroot, htdoc, webroot等等这些文件。在使用Perfect开发服务器端时,亦是如此,我们可以在Source目录中的main.swift中对静态文件的根目录进行配置,下方就是我们的配置代码:

    3、数据类型

    整形值:Int

    在32位平台上, Int 的长度和 Int32 相同。在64位平台上, Int 的长度和 Int64 相同。

    let minInt:Int = -2_147_483_648   //整数和浮点数都可以添加额外的零或者添加下划线来增加代码的可读性。
    let minValue = Int32.min
    print(minValue)                   // -2147483648
    

    浮点数值:Double、Float

    Double代表 64 位的浮点数。Float 代表 32 位的浮点数。

    布尔量值:Bool

    Swift为布尔量提供了两个常量值:true 和 false 。

    Swift的类型安全机制会阻止用一个非布尔量的值替换掉 Bool 。下面的栗子在编译时会报错:

    let i = 1
    if i {
        // this example will not compile, and will report an error
    }
    

    字符串值:String

    字符串的拼接最常用的有:1、使用 “+”号,如Java、JavaScript。2、使用占位符或函数(如append方法),如OC。

    Swift还支持用转义字符加小括号()的方式。

    let apples = 3
    let oranges = 5
    let appleSummary = "I have (apples) apples."
    let fruitSummary = "I have (apples   oranges) pieces of fruit."
    

    为什么是 :服务端开发,Mustache页面模板与日志记录。是转义字符,大家都知道啊。
    为什么是(),而不是[]、{}:在小括号里面做数据运算等操作是如此的顺其自然、优雅。

    Swift同样支持使用 和append方法进行字符串的拼接。

    数组:Array

    Swift 数组类型完整的写法是 Array<Element>, Element是数组允许存入的值的类型。你同样可以简写数组的类型为 [Element]。

    使用初始化器创建数组

    var someInts = [Int]()
    print("someInts is of type [Int] with (someInts.count) items.")
    // prints "someInts is of type [Int] with 0 items."
    
    var threeDoubles = Array(repeating: 0.0, count: 3)
    // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
    

    使用数组字面量创建数组

    var shoppingList = ["catfish", "water", "tulips", "blue paint"]
    shoppingList[1] = "bottle of water"
    

    集合:Set

    Swift 的集合类型写做 Set<Element>,这里的 Element是合集要储存的类型。不同与数组,合集没有等价的简写。

    var letters = Set<Character>()
    print("letters is of type Set<Character> with (letters.count) items.")
    // prints "letters is of type Set<Character> with 0 items."
    

    字典:Dictionary

    Swift 字典类型完整的写法是 Dictionary<Key, Value>,简写形式:[Key: Value]。

    使用初始化器创建字典

    var namesOfIntegers = [Int: String]()
    // namesOfIntegers is an empty [Int: String] dictionary
    

    使用字典字面量创建字典

    var occupations = 
    [
       "Malcolm": "Captain",
       "Kaylee": "Mechanic",
    ]
    occupations["Jayne"] = "Public Relations"
    

    元组类型:用来创建复合值,作为返回值、参数等。小括号内放置n个数据,任何类型的组合都是可以的。为什么是小括号--中括号和大括号的意义被占用了。

    let (x, y) = (1, 2)
    // x is equal to 1, and y is equal to 2
    

    存和取:

    let http404Error = (404, "Not Found")
    let (statusCode, statusMessage) = http404Error
    print("The status code is (statusCode)")
    print("The status code is (http404Error.0)")
    

    存和取:

    let http200Status = (statusCode: 200, description: "OK")
    print("The status code is (http200Status.statusCode)")
    

    类型别名

    类型别名可以为已经存在的类型定义了一个新的可选名字。用 typealias 关键字定义类型别名。

    typealias NameType = String
    var name: NameType = "Kobe"
    

    1.Mustache页面模板的引入

    1.Mustache页面模板的引入

      新葡亰496net 1

    4、控制流

    Mustache对应的模板地址包如下:

    Mustache对应的模板地址包如下:

    配置完成后,如果我们的项目不是用Xcode进行管理的话,当我们对Perfect工程进行编译和运行时,会在相应的模板目录下创建相应的静态文件的根目录(webroot)。如下所示:

    4.1 if语句

    let individualScores = [75, 43, 103, 87, 12]
    var teamScore = 0
    for score in individualScores 
    {
        if score > 50 
        {
            teamScore  = 3
        } 
        else 
        {
            teamScore  = 1
        }
    }
    print(teamScore)
    

    重要语法:可选

    Swift新增一个“可选”的概念,在一个值的类型后面加上一个问号来把某个值标记为可选。只有可选的值才有可能被赋值为nil。

    var optionalString:String? = "Hello"
    print(optionalString == nil)
    

    Swift 中的 nil 和Objective-C 中的 nil 不同,在 Objective-C 中 nil是一个指向不存在对象的指针。在 Swift中, nil 不是指针,他是值缺失的一种特殊类型,任何类型的可选项都可以设置成 nil 而不仅仅是对象类型。

    nil 不能用于非可选的常量或者变量,如果你的代码中变量或常量需要作用于特定条件下的值缺失,可以给他声明为相应类型的可选项。

    可选项绑定

    if 和 let搭配使用可以替代非空操作。使用可选绑定而不是强制展开来重写 possibleNumber。

    if let actualNumber = Int(possibleNumber) 
    {
        print(""(possibleNumber)" has an integer value of (actualNumber)")
    } 
    else 
    {
        print(""(possibleNumber)" could not be converted to an integer")
    }
    

    可选值的强制展开

    一旦确定可选中包含值,你可以在可选的名字后面加一个感叹号 ( ! ) 来获取值,感叹号的意思是“我知道这个可选项里边有值,展开吧。”这就是所谓的可选值的强制展开。

    if convertedNumber != nil 
    {
       print("convertedNumber has an integer value of (convertedNumber!).")
    }
    

    使用 ! 来获取一个不存在的可选值会导致运行错误,在使用!强制展开之前必须确保可选项中包含一个非 nil 的值。

    guard 语句

    类似于 if 语句,基于布尔值表达式来执行语句。使用 guard 语句来要求一个条件必须是真才能执行 guard 之后的语句。与 if 语句不同, guard 语句总是有一个 else 分句—— else 分句里的代码会在条件不为真的时候执行。

    func greet(person: [String: String]) 
    {
        guard let name = person["name"] 
        else 
        {
            return
        }
    
        print("Hello (name)!")
    
        guard let location = person["location"] 
        else 
        {
            print("I hope the weather is nice near you.")
            return
        }
    
        print("I hope the weather is nice in (location).")
    }
    
    greet(["name": "John"])
    // prints "Hello John!"
    // prints "I hope the weather is nice near you."
    greet(["name": "Jane", "location": "Cupertino"])
    // prints "Hello Jane!"
    // prints "I hope the weather is nice in Cupertino."
    

    如果 guard 语句的条件被满足,代码会继续执行直到 guard 语句后的花括号。任何在条件中使用可选项绑定而赋值的变量或者常量在 guard 所在的代码块中随后的代码里都是可用的。

    如果这个条件没有被满足,那么在 else 分支里的代码就会被执行。这个分支必须转移控制结束 guard 所在的代码块。要这么做可以使用控制转移语句比如 return , break , continue 或者 throw ,或者它可以调用一个不带有返回值的函数或者方法,比如 fatalError() 。

    .Package(
    url: "",
    majorVersion: 2, minor: 0
    )

    .Package(
    url: "",
    majorVersion: 2, minor: 0
    )

      新葡亰496net 2

    4.2 Switch

    case支持了更多的数据类型,default是必要的且后面至少要有一条语句。

    break能不能不写?为了让代码更加安全、优雅、简洁,答案是可以。

    let vegetable = "red pepper"
    switch vegetable 
    {
        case "celery":
            print("Add some raisins and make ants on a log.")
        case "cucumber", "watercress":
            print("That would make a good tea sandwich.")
        case let x where x.hasSuffix("pepper"):
            print("Is it a spicy (x)?")
        default:
            print("Everything tastes good in soup.")
    }
    

    我们需要在Package.swift文件中进行依赖包的配置,如下所示。引入包后,使用命令行进行编译:"swift build"即可。如果你的项目是使用Xcode进行编译的话,还需要重新执行“swift package generate-xcodeproj”命令再次生成xcodeproj文件即可。

    我们需要在Package.swift文件中进行依赖包的配置,如下所示。引入包后,使用命令行进行编译:"swift build"即可。如果你的项目是使用Xcode进行编译的话,还需要重新执行“swift package generate-xcodeproj”命令再次生成xcodeproj文件即可。

    在webroot中我们添加上相应的静态文件,我们就可以通过Perfect服务进行访问了,下方是我们在webroot中添加的index.html的内容,用浏览器就可以访问了。下方就是我们比较简单的index.html中的内容了,当然下方的html简单的不能在简单了,如下所示:

    4.3 for-in

    let interestingNumbers = [
       "Prime": [2, 3, 5, 7, 11, 13],
       "Fibonacci": [1, 1, 2, 3, 5, 8],
       "Square": [1, 4, 9, 16, 25],
    ]
    var largest = 0
    for (kind, numbers) in interestingNumbers 
    {
       for number in numbers 
       {
           if number > largest 
           {
               largest = number
           }
       }
    }
    print(largest)
    

      新葡亰496net 3

      新葡亰496net 4

      新葡亰496net 5

    4.4 while

    var n = 2
    while n < 100 
    {
       n = n * 2
    }
    print(n)
    
    var m = 2
    repeat 
    {
       m = m * 2
    } while m < 100
    print(m)
    

    repeat-while 类似do-while,在每次循环结束的时候计算它自己的条件。

     

     

    在访问上述index.html文件之前,我们需要将main.swift中添加的路由进行删除,下方选中的部分就是要删除的代码。如果你不删除下方这段代码的话,如果你访问localhost:8181的话,那么就是下方路由所返回的内容。修改完代码后,要重新进行编译运行才生效的,这一点与解释性语言PHP是不同的。

    新葡亰496net,5、函数和闭包

    2.Mustache页面模板的使用

    2.Mustache页面模板的使用

      新葡亰496net 6

    5.1 定义和调用函数

    定义了一个函数的时候,你可以选择定义一个或者多个命名的分类的值作为函数的输入(所谓的形式参数),并且/或者定义函数完成后将要传回作为输出的值的类型(所谓它的返回类型)。

    关键字是function还是func?基于简洁的原则,选择了更简洁的 func。

    Swift重新定义了返回值。

    func greet(person:String , day: String) -> String
    {
       return "Hello (person), today is (day)."
    }
    greet(person: "Bob", day: "Tuesday")
    

    首先我们在相应的静态页面中添加上相应的标签,我们还是以上篇博客中使用到的index.html为例,在该html页面中,添加上相应的标签,该标签会被替换成相应的内容。下方的标签为{{title}}。

    首先我们在相应的静态页面中添加上相应的标签,我们还是以上篇博客中使用到的index.html为例,在该html页面中,添加上相应的标签,该标签会被替换成相应的内容。下方的标签为{{title}}。

    经过上述步骤后,我们就可以通过访问localhost:8181来加载我们的index.html文件了,如下所示:

    5.2 参数标签和参数名

    参数格式:(参数标签 参数名:参数类型)

    默认情况下,函数使用他们的形式参数名来作为实际参数标签。

    在形式参数前可以写自定义的实际参数标签,或者使用 _ 来避免使用实际参数标签。

    func greet(_ person:String, on day:String) ->String
    {
       return "Hello (person), today is (day)."
    }
    greet("John", on: "Wednesday")
    

      新葡亰496net 7

      新葡亰496net 8

      新葡亰496net 9

    5.3 输入输出形式参数

    在形式参数定义开始的时候在前边添加一个 inout关键字可以定义一个输入输出形式参数。
    输入输出形式参数能输入值到函数,函数能对其进行修改,还能输出到函数外边替换原来的值。

    只能把变量作为输入输出形式参数的实际参数。不能用常量或者字面量作为实际参数,因为常量和字面量不能修改。
    在将变量作为实际参数传递给输入输出形式参数的时候,直接在它前边添加一个和符合 ( &) 来明确可以被函数修改。

    func swapTwoInts(_ a: inout Int, _ b: inout Int) 
    {
       let temporaryA = a
       a = b
       b = temporaryA
    }
    
    var someInt = 3
    var anotherInt = 107
    swapTwoInts(&someInt, &anotherInt)
    print("someInt is now (someInt), and anotherInt is now (anotherInt)")
    // Prints "someInt is now 107, and anotherInt is now 3"
    

    处理完静态页面,然后我们该处理服务端的代码了。下方代码是从Prefect的官方文档上摘下来的,不过改了一下Value的key。所有处理Mustache页面模板的句柄都需要继承自MustachePageHandler,在extendValuesForResponse()方法中进行模板变量的处理,如下所示:

    处理完静态页面,然后我们该处理服务端的代码了。下方代码是从Prefect的官方文档上摘下来的,不过改了一下Value的key。所有处理Mustache页面模板的句柄都需要继承自MustachePageHandler,在extendValuesForResponse()方法中进行模板变量的处理,如下所示:

    2、Xcode管理下的Perfect的静态文件目录

    5.4 函数作为返回值、参数

    这时候的语法只需要(传入什么,返回什么):(参数类型)-> 返回类型

    作为返回值:

    fun makeIncrementer() -> ((Int) -> Int) 
    {
       func addOne(number:Int) ->Int
       {
           return 1   number
       }
       return addOne
    }
    var increment = makeIncrementer()
    increment(7)
    

    作为参数:

    func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool
    {
       for item in list 
       {
           if condition(item) 
           {
               return true
           }
       }
       return false
    }
    

      新葡亰496net 10

      新葡亰496net 11

    上述步骤是未使用Xcode管理Perfect项目的情况,webroot文件夹就位于工程目录下。当我们的Perfect工程使用Xcode进行管理后,webroot的位置就会被改变。下方就是调用swift的包管理器生成相应的xcodeproj文件:

    6、闭包

    闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用,这也就是所谓的闭合并包裹那些常量和变量,因此被称为“闭包”。

    函数其实就是闭包的一种特殊形式:一段可以被随后调用的代码块。

    闭包表达式语法一般形式:

    { (parameters) -> (return type) in
        statements
    }
    

    使用花括号({})括起一个没有名字的闭包。

    使用 in来分隔函数体和实际参数、返回类型。(为什么选择这么做?)

    numbers.map(
    {
       (number:Int) -> Int in
       let result = 3 * number
       return result
    })
    

    当一个闭包的类型已经可知,可以去掉它的参数类型、返回类型,或者都去掉。

    let mappedNumbers = numbers.map({ number in 3 * number })
    print(mappedNumbers)
    

    下方是对index.html进行重定向,将index.html交给上述的处理句柄进行处理。

    下方是对index.html进行重定向,将index.html交给上述的处理句柄进行处理。

      新葡亰496net 12

    7、对象和类(class)

    类的定义和使用,使用class关键字定义类

    class Shape 
    {
       var numberOfSides = 0
       func simpleDescription() -> String
       {
           return "A shape with (numberOfSides) sides."
       }
    }
    
    var shape = Shape()
    shape.numberOfSides = 7
    var shapeDescription = shape.simpleDescription()
    

    基于简洁的原则,很多语言采用的关键字new是不需要的。

    重新定义了import机制:使用同一个模块(module)的swift文件不需要import。import反而编译会报“No such Module”错误。

    init来创建一个初始化器,deinit来创建一个反初始化器。

    class NamedShape 
    {
       var numberOfSides:Int = 0
       var name:String          
       init(name:String) 
       {
           self.name = name
       }
    
       func simpleDescription() -> String
       {
           return "A shape with (numberOfSides) sides."
       }
    }
    

    子类的方法如果要重写父类的实现,则需要使用override——不使用override关键字来标记则会导致编译器报错。

    class Square:NamedShape
    {
       var sideLength:Double 
    
       init(sideLength:Double , name:String) 
       {
           self.sideLength = sideLength
           super.init(name: name)
           numberOfSides = 4
       }
    
       func area() -> Double
       {
           return sideLength * sideLength
       }
    
       override func simpleDescription() -> String
       {
           return "A square with sides of length (sideLength)."
       }
    }
    
    let test = Square(sideLength: 5.2, name: "my test square")
    test.area()
    test.simpleDescription()
    

    Getter 和 Setter

    class EquilateralTriangle: NamedShape
    {
       var sideLength: Double = 0.0
    
       init(sideLength:Double , name:String) 
       {
           self.sideLength = sideLength
           super.init(name: name)
           numberOfSides = 3
       }
    
       var perimeter:Double 
       {
            get {return 3.0 * sideLength}
            set {sideLength = newValue / 3.0}
       }
    
       override func simpleDescription() -> String 
       {
           return "An equilateral triangle with sides of length (sideLength)."
       }
    }
    

    如果不需要计算属性但仍然需要在设置一个新值的前后执行代码,使用 willSet和 didSet。

    class TriangleAndSquare 
    {
       var triangle: EquilateralTriangle
       {
           willSet {square.sideLength = newValue.sideLength}
       }
       var square:Square 
       {
           willSet {triangle.sideLength = newValue.sideLength}
       }
       init(size:Double, name:String) 
       {
           square = Square(sideLength: size, name: name)
           triangle = EquilateralTriangle(sideLength: size, name: name)
       }
    }
    
    var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
    print(triangleAndSquare.square.sideLength)
    print(triangleAndSquare.triangle.sideLength)
    triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
    print(triangleAndSquare.triangle.sideLength)
    

      新葡亰496net 13

      新葡亰496net 14

    如果你的项目目录中有webroot目录,也有index.html文件。但是使用xcode进行Perfect项目管理后,就找不到下方这个index.html文件了。也就是webroot的文件目录变地方了。这一点在官方文档上是没有介绍的。

    8、枚举和结构体

    上述实例的运行结果如下,{{title}}被换成了Swift用户,如下所示:

    上述实例的运行结果如下,{{title}}被换成了Swift用户,如下所示:

        新葡亰496net 15

    8.1 枚举(Enumerations)

    使用 enum来创建枚举

    enum CompassPoint 
    {
        case north
        case south
        case east
        case west
    }
    

    不像 C 和 Objective-C 那样,Swift 的枚举成员在被创建时不会分配一个默认的整数值。在上面 CompassPoint的例子中, north, south, east和 west并不代表 0, 1, 2和 3,它们在自己的权限中都是完全合格的值。

    枚举能够包含方法!!!

    enum Rank:Int
    {
       case ace = 1
       case two, three, four, five, six, seven, eight, nine, ten
       case jack, queen, king
       func simpleDescription() -> String 
       {
           switch self 
           {
           case .ace:
               return "ace"
           case .jack:
               return "jack"
           case .queen:
               return "queen"
           case .king:
               return "king"
           default:
               return String(self.rawValue)
           }
       }
    }
    
    let ace = Rank.ace          
    print(ace)              //print ace
    

    每个枚举都定义了一个全新的类型。正如 Swift 中其它的类型那样,它们的名称(例如: CompassPoint和 Planet)需要首字母大写。给枚举类型起一个单数的而不是复数的名字,从而使得它们能够顾名思义。

    当与 CompassPoint中可用的某一值一同初始化时 directionToHead的类型会被推断出来。一旦 directionToHead以 CompassPoint类型被声明,你就可以用一个点语法把它设定成不同的 CompassPoint值。

    var directionToHead = CompassPoint.west
    directionToHead = .east
    

    directionToHead的类型是已知的,所以当设定它的值时你可以不用写类型。这样做可以使得你在操作确定类型的枚举时让代码非常易读。

      新葡亰496net 16

      新葡亰496net 17

    通过搜索,我们可以找到Xcode管理下的Perfect项目中的webroot目录,使用Xcode打开Perfect工程,找到Product,然后右键单击,Show In Finder。

    8.1 结构体(Structures)

    使用 struct来创建结构体,结构体提供很多类似与类的行为,包括方法和初始化器。

    struct Card 
    {
        var rank: Rank
        var suit: Suit
        func simpleDescription() -> String 
        {
            return "The (rank.simpleDescription()) of (suit.simpleDescription())"
       }
    }
    
    let threeOfSpades = Card(rank: .three, suit: .spades)
    let threeOfSpadesDescription = threeOfSpades.simpleDescription()
    

    结构体和类最重要的一点区别就是结构体总是:结构体(和枚举)是值类型,类是引用类型。结构体会在传递的时候拷贝其自身,而类则会传递引用。

     

     

      新葡亰496net 18

    9、协议和扩展

    二、日志系统的配置与使用

    二、日志系统的配置与使用

    我们会看到Xcode管理下的webroot目录,如下所示。你可以将静态文件放到wwwroot中进行访问。

    9.1 协议

    使用 protocol来声明协议。

    protocol ExampleProtocol 
    {
        var simpleDescription:String{ get }
        mutating func adjust()
    }
    

    类,枚举以及结构体都可实现(adopt)协议。

    类实现协议

    class SimpleClass:ExampleProtocol 
    {
        var simpleDescription:String = "A very simple class."
        var anotherProperty:Int = 69105
        func adjust() 
        {
            simpleDescription  = "Now 100% adjusted."
        }
    }
    
    var a = SimpleClass()
    a.adjust()
    let aDescription = a.simpleDescription
    

    结构体实现协议

    struct SimpleStructure: ExampleProtocol
    {
       var simpleDescription: String = "A simple structure"
       mutating func adjust() 
       {
           simpleDescription  = " (adjusted)"
       }
    }
    
    var b = SimpleStructure()
    b.adjust()
    let bDescription = b.simpleDescription
    

    使用mutating关键字来声明在SimpleStructure中使方法可以修改struct或enum的变量。

    在 SimpleClass中则不需要这样声明,因为类里的方法总是可以修改其自身属性的。

    日志系统在服务端开发中是不可或缺的,接下来我们要做的事情是讲每次请求或者相应的内容写入到日志文件中,当然平时调试打印的一些信息也可以输出到日志文件中。接下来,我们就要来介绍一下Perfect框架中日志系统的引入和使用方式。

    日志系统在服务端开发中是不可或缺的,接下来我们要做的事情是讲每次请求或者相应的内容写入到日志文件中,当然平时调试打印的一些信息也可以输出到日志文件中。接下来,我们就要来介绍一下Perfect框架中日志系统的引入和使用方式。

      新葡亰496net 19

    9.2 扩展

    使用 extension 来给现存的类型增加功能。比如说新的方法和计算属性。

    extension Int: ExampleProtocol 
    {
        var simpleDescription: String 
        {
            return "The number (self)"
        }
    
        mutating func adjust() 
        {
            self  = 42
        }
    }
    print(7.simpleDescription)
    

     

     

    当我们在此刷新浏览器时,就可以找到相应的index.html.

    10、错误处理

    用任何遵循 Error 协议的类型来表示错误。

    enum PrinterError:Error 
    {
        case outOfPaper
        case noToner
        case onFire
    }
    

    用 throw 来抛出一个错误。
    用 throws 来说明一个函数可以抛出错误。

    func send(job:Int, toPrinter printerName: String) throws ->String
    {
        if printerName == "Never Has Toner" 
        {
            throw PrinterError.noToner
        }
        return "Job sent"
    }
    

    do-catch

    在 do 代码块里,你用 try 来在能抛出错误的函数前标记。
    在 catch 代码块,错误会自动赋予名字 error ,如果你不给定其他名字的话。

    do 
    {
        let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
        print(printerResponse)
    } 
    catch
    {
        print(error)
    }
    

    可以提供多个 catch 代码块来处理特定的错误。你可以在 catch 后写一个模式,用法和 switch 语句里的 case 一样。

    do 
    {
        let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
        print(printerResponse)
    } 
    catch PrinterError.onFire 
    {
        print("I'll just put this over here, with the rest of the fire.")
    } 
    catch let printerError as PrinterError
    {
        print("Printer error: (printerError).")
    } 
    catch 
    {
        print(error)
    }
    

    1.引入日志相关包

    1.引入日志相关包

      新葡亰496net 20

    11、泛型

    把名字写在尖括号里来创建一个泛型方法或者类型。

    func makeArray<Item>(repeating item:Item, numberOfTimes:Int) -> [Item] 
    {
        var result = [Item]()
        for _ in 0..< numberOfTimes 
        {
            result.append(item)
        }
        return result
    }
    makeArray(repeating: "knock", numberOfTimes:4)
    

    可以从函数和方法、类、枚举、结构体创建泛型。

    enum OptionalValue<Wrapped> 
    {
        case none
        case some(Wrapped)
    }
    var possibleInteger:OptionalValue<Int> = .none
    possibleInteger = .some(100)
    

    在类型名称后加 where可用来明确一些需求——比如要求类型实现一个协议,要求两个类型必须相同,要求类必须继承自特定的父类。

    func anyCommonElements<T:Sequence , U:Sequence>(_ lhs: T, _ rhs: U) -> Bool
        where T.Iterator.Element:Equatable,T.Iterator.Element == U.Iterator.Element
        {
            for lhsItem in lhs 
            {
                for rhsItem in rhs 
                {
                    if lhsItem == rhsItem 
                    {
                        return true
                    }
                }
            }
            return false
    }
    anyCommonElements([1, 2, 3], [3])
    
    <T: Equatable>和 <T where T: Equatable>是一样的。
    

    要做到上述的功能,我们需要引入下方的两个依赖包。引入包后与上述页面模板的做法一致,还是需要重新使用命令行编译和运行工程的,当然要重新生成xcodeproj文件。在此就不做过多赘述了。

    要做到上述的功能,我们需要引入下方的两个依赖包。引入包后与上述页面模板的做法一致,还是需要重新使用命令行编译和运行工程的,当然要重新生成xcodeproj文件。在此就不做过多赘述了。

     

    转行篇

    目前主要的 Swift 服务端开发框架有:Perfect(perfect.org)、Vapor(vapor.codes)、Kitura(kitura.io) 和 Zewo(zewo.io)。

    Perfect是目前最流行的框架,这里也只介绍 Perfect

    //Request请求日志过滤器
    .Package(url: "",
    majorVersion: 0),

    //将日志写入指定文件
    .Package(url: "",
    majorVersion: 0, minor: 0),

    //Request请求日志过滤器
    .Package(url: "",
    majorVersion: 0),

    //将日志写入指定文件
    .Package(url: "",
    majorVersion: 0, minor: 0),

    二、路由配置

    1、Perfect简介

    包含:完整强大工具箱、软件框架、Web应用服务器。

    可运行平台:Linux、iOS和MacOS (OS X)。

    可用于:开发Web应用和REST服务。

     

     

    下方这段代码就是main.swift中的代码段,首先创建了一个HTTPServer()的对象,然后又创建了一个路由对象,接着又将这个路由对象添加到了服务对象上。具体代码如下所示:

    2、Perfect示例

    (1)配置环境(MacOS下有Xcode 8.0 即可)。(2)下载示例Demo。(3)编译运行。

    执行以下命令能够克隆并编译一个空的入门项目。编译后可以启动一个本地的服务器,监听计算机的8181端口。

    git clone https://github.com/PerfectlySoft/PerfectTemplate.git
    cd PerfectTemplate
    swift build
    .build/debug/PerfectTemplate
    

    使用SPM生成一个Xcode项目

    Swift软件包管理器(SPM) 是一个用于Swift项目开发、测试、依存关系管理的命令行工具。所有的Perfect组件针对SPM设计的。如果使用Perfect进行项目开发,一定需要SPM。

    SPM根据 Package.swift 文件可以创建对应的Xcode项目,该项目允许您使用Xcode编译和调试。在项目根目录下使用以下命令行:

    swift package generate-xcodeproj
    

    以上clone下来的项目代表了一个基本的Swift服务器项目,下边介绍下这个项目。

    新葡亰496net 21

    打开PerfectTemplate.xcodeproj工程文件,编译。这时可能会报错(Xcode8.1后修复了)。

    ld: library not found for -lCOpenSSL for architecture x86_64
    

    解决该问题的方法就是设置 Library Search Paths 为 "$(PROJECT_DIR)/**"(包括双引号也)

    2.将日志写入文件

    2.将日志写入文件

      新葡亰496net 22

    3、Perfect目录

    在Perfect Template项目模板中,有两个很重要的文件:

    (1)Sources目录:包含了所有Perfect项目的Swift源程序文件。

    (2)Package.swift:SPM文件管理清单,包含了整个项目对其它库函数的依存关系。这个文件可以理解为CocoaPod中的Podfile。

    (3)webroot:存储相应的静态文件的根目录。可以在Source目录中的main.swift中对静态文件的根目录进行配置。

    所有的SPM项目至少要包括一个 Sources 目录和一个 Package.swift 文件。

    新葡亰496net 23

    在 Package.swift 文件中由两个重要的内容可能需要编辑。
    第一个是 name 项目名称,用于说明当前项目的目标名称,因此最后可执行文件的名字也会按照这个名称进行编译。

    第二个是 dependencies 依存关系清单。该内容说明了您的应用程序需要的所有子项目列表,在这个数组中其中每一个条目都包含了一个“.Package”软件包,及其来源URL和版本。

    新葡亰496net 24

    接下来我们就可以调用日志系统将日志写入指定的文件了,首先我们需要检查存储日志文件的目录是否存在,如果不存在就创建。创建完毕后,将该目录赋值给日志系统的文件目录即可,如下第一个框中的内容。然后添加请求和相应的日志过滤器。最下方的框是LogFile的几个方法的使用,这些信息都会被存储到相应的文件。

    接下来我们就可以调用日志系统将日志写入指定的文件了,首先我们需要检查存储日志文件的目录是否存在,如果不存在就创建。创建完毕后,将该目录赋值给日志系统的文件目录即可,如下第一个框中的内容。然后添加请求和相应的日志过滤器。最下方的框是LogFile的几个方法的使用,这些信息都会被存储到相应的文件。

     

    4、用 Perfect 写一个 RESTful API

    功能:实现表单提交,返回Json数据。

      新葡亰496net 25 

      新葡亰496net 26 

    1.添加静态路由路径

    1、静态文件的添加与访问

    在PHP开发或者Java Web开发中,都有一个根目录来存储相应的静态文件,比如wwwroot, htdoc, webroot等等这些文件。使用Perfect开发服务器端也是如此,我们可以在Source目录中的main.swift中对静态文件的根目录进行配置,下方就是我们的配置代码:

    // Set a document root.
    // This is optional. If you do not want to serve static content then do not set this.
    // Setting the document root will automatically add a static file handler for the route /**
    server.documentRoot = "./webroot"
    

    配置完成后,如果我们的项目不用Xcode进行管理的话,当我们对Perfect工程进行编译和运行时,会在相应的模板目录下创建相应的静态文件的根目录(webroot)。如下所示:

    在webroot中我们添加上相应的静态文件,我们就可以通过Perfect服务进行访问了,下方是index.html的内容——一个提交帐号密码的表单。

    <html>
    <head>
        <title>Login</title>
    </head>
    <body>
        <h1>Login</h1>
        <form action = "http://0.0.0.0:8181/login" method = "POST">
            UserName : <input type = "text" name = "userName"><br/>
            Password : <input type = "text" name = "password"><br/>
            <input type = "submit" value = "Submit">
        </form>
    </body>
    </html>
    

    在访问上述index.html文件之前,我们需要将main.swift中添加的路由进行删除,下方选中的部分就是要删除的代码。如果你不删除下方这段代码的话,如果你访问localhost:8181的话,那么就是下方路由所返回的内容。修改完代码后,要重新进行编译运行才生效的,这一点与解释性语言PHP是不同的。

    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, Jinghuang Liu!</body></html>")
            response.completed()
        }
    )
    

    经过上述步骤后,我们就可以通过访问localhost:8181来加载我们的index.html文件了。

    http://0.0.0.0:8181
    

    进行上述配置后,我们还需要将RequestLogger()中的日志输出形式切换成LogFile的形式,如下所示:

    进行上述配置后,我们还需要将RequestLogger()中的日志输出形式切换成LogFile的形式,如下所示:

    我们可以通过调用routes对象的add()方法为路由添加各种路径,当然add()有好多重载的方法,不过其功能都是往路由上添加路径、请求方法或者创立句柄。

    2、路由配置

    main.swift中的代码段,首先创建了一个HTTPServer()的对象,然后又创建了一个路由对象,接着又将这个路由对象添加到了服务对象上。

    下方我们添加了一个“/login”的路由路径,其请求方法是.post的方式,后方的闭包是对响应的处理。具体代码如下:

    routes.add(method: .post,
               uri: "/login",
               handler:{request, response in
    
                //账户密码到验证
                guard let userName = request.param(name:"userName") else
                {
                    return
                }
                guard let password = request.param(name:"password") else
                {
                    return
                }
    
                //json的包装:可以用一个专门的类来做
                var resBody = ["userName" : userName , "password" : password]
                let responseDic : [String : Any] = ["responseBody" : resBody,
                                                    "result" : "Success",
                                                    "Message" : "request Successfully"]
                do
                {
                    let json = try responseDic.jsonEncodedString()
                    response.setBody(string:json)
                }
                catch
                {
                    response.setBody(string:"json encode error")
                }
    
                response.completed()
        }
    )
    

    表单提交后会跳到以下的地址

    http://0.0.0.0:8181/login
    

    得到以下结果

    {"responseBody":{"userName":"ljh","password":"123456"},"result":"Success","Message":"request Successfully"}
    

      新葡亰496net 27

      新葡亰496net 28

      新葡亰496net 29

    参考:

    https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0

    http://perfect.org/docs/index.html

    https://swift.org/

    配置完毕后,我们就可以使用日志系统进行日志输出了,最终结果如下所示:

    配置完毕后,我们就可以使用日志系统进行日志输出了,最终结果如下所示:

     

      新葡亰496net 30

      新葡亰496net 31

    下方我们添加了一个“/login”的路由路径,其请求方法是.get的方式。后方的尾随闭包是对响应的处理。具体代码如下:

     

     

      新葡亰496net 32

    今天的博客就先到这而把,下篇博客会介绍MySql的连接与相关操作。

    今天的博客就先到这而把,下篇博客会介绍MySql的连接与相关操作。

    重新编译并运行我们的Perfect工程,然后访问localhost:8181/login路径,会在浏览器中看到下方的返回信息。其他的路由

      新葡亰496net 33

     

    2、路由变量

    在配置路由时,我们的路径中可以添加路由变量来匹配同一类型的url。下方就是Perfect框架中路由变量的使用。在URL中通过{key}的方式来插入路由变量。下方的uri中中间的{(valueKey)}就是通过字符串插值的方式插入的路由变量的Key,我们可以通过这个key来获取到相应的路由变量。

      新葡亰496net 34

    下方就是我们访问上述路径的结果:

      新葡亰496net 35

     

    3、路由通配符

    我们还可以在URI中使用通配符类匹配路径,使用*好来匹配通用的路径。下方就是路由通配符的使用方式:

      新葡亰496net 36

    下方就是路由通配符是运行结果:

      新葡亰496net 37

     

    4.结尾通配符

    下方就是结尾通配符的使用方式,结尾处使用“**”来匹配尾部所有符合规则的uri, 然后通过routeTrailingWildcardKey来获取通配的内容。

      新葡亰496net 38

    访问结果如下所示:

      新葡亰496net 39

    上述这四种路由的优先级为:路由变量 > 静态路由 > 通配符路径 > 结尾通配符。官方文档上还有如果配置路由表的版本的示例,在此就不做过多赘述了。

     

    三、表单提交与Json返回

    聊完路由的配置,接下来我们要聊一下表单的提交与json数据的返回。本部分内容我们首先使用form表单post提交的方式将数据提交到指定的路由并处理,处理完毕后再讲处理的数据通过josn的数据格式返回给客户端。

    1、构建form表单

    首先我们得创建相应的表单提交的代码,当然代码比较简单,就一个from表单。action的地址就是我们在服务器端配置的路由地址“127.0.0.1:8181/login”,而表单提交的方式是POST。具体如下所示。

      新葡亰496net 40

     

    2、获取form表单参数

    下方就是服务器端获取表单参数的代码。首先我们添加了一个“/login”路由,路由的请求方式是.post。首先我们获取请求的参数。因为form表单的请求参数名是“userName”和“password”,所以我们要使用这两个name从request对象中获取相应的值。获取到值后你可以查询数据库或者其他一些操作呢。操作完毕后返回相应的json信息。Swift中的字典是可以调用jsonEncodeString()方法来转换的,后边的博客会对部分Perfect框架的源码进行解析,其中会涉及到如何将字典转换成json串,在此就不做过多赘述了。

      新葡亰496net 41

     

    3.表单提交与结果返回

    上面web页和服务端都已实现完毕,接下来我们就开始测试了。下方就是相应的form表单与结果返回:

       新葡亰496net 42

     本篇博客就先到这儿,下篇博客仍然会对Perfect框架的相关内容进行介绍。

    本文由新葡亰496net发布于奥门新萄京娱乐场,转载请注明出处:0服务端开发,Mustache页面模板与日志记录

    关键词: