入门篇主要讲述Swift语法,转行篇主要讲述用Swift编写服务器程序。
入门篇
Swift语法的设计原则:简洁、安全、现代化。
基于简洁的原则:每条语句后的分号可以不用写了(写上分号也没错),OC数据类型字面量语法中的@符号去了,import语句简洁到令人窒息。
基于安全原则:字典的value为nil不会崩溃。
基于现代化原则:加入了泛型等。
1、打印:
还能想到比print更合适的方式么
print("Hello, world!")
2、常量(constant)和变量(variable)
声明常量用let,变量用var。
var myVariable = 42
let myConstant = 42
let explicitDouble: Double = 70
可以在声明一个变量或常量的时候提供类型标注,来明确变量或常量能够储存值的类型。添加类型标注的方法是在变量或常量的名字后边加一个冒号,再跟上要使用的类型名称。
可变数据类型(如数组、字典等)用var,不可变数据类型用let。
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."
为什么是 \:\是转义字符,大家都知道啊。
为什么是(),而不是[]、{}:在小括号里面做数据运算等操作是如此的顺其自然、优雅。
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"
4、控制流
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() 。
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.")
}
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)
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,在每次循环结束的时候计算它自己的条件。
5、函数和闭包
5.1 定义和调用函数
定义了一个函数的时候,你可以选择定义一个或者多个命名的分类的值作为函数的输入(所谓的形式参数),并且/或者定义函数完成后将要传回作为输出的值的类型(所谓它的返回类型)。
关键字是function还是func?基于简洁的原则,选择了更简洁的 func。
Swift重新定义了返回值。
func greet(person:String , day: String) -> String
{
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")
5.2 参数标签和参数名
参数格式:(参数标签 参数名:参数类型)
默认情况下,函数使用他们的形式参数名来作为实际参数标签。
在形式参数前可以写自定义的实际参数标签,或者使用 _ 来避免使用实际参数标签。
func greet(_ person:String, on day:String) ->String
{
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
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"
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
}
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)
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)
8、枚举和结构体
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的类型是已知的,所以当设定它的值时你可以不用写类型。这样做可以使得你在操作确定类型的枚举时让代码非常易读。
8.2 结构体(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()
结构体和类最重要的一点区别就是结构体总是:结构体(和枚举)是值类型,类是引用类型。结构体会在传递的时候拷贝其自身,而类则会传递引用。
9、协议和扩展
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中则不需要这样声明,因为类里的方法总是可以修改其自身属性的。
9.2 扩展
使用 extension 来给现存的类型增加功能。比如说新的方法和计算属性。
extension Int: ExampleProtocol
{
var simpleDescription: String
{
return "The number \(self)"
}
mutating func adjust()
{
self += 42
}
}
print(7.simpleDescription)
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)
}
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>是一样的。
转行篇
目前主要的 Swift 服务端开发框架有:Perfect(perfect.org)、Vapor(vapor.codes)、Kitura(kitura.io) 和 Zewo(zewo.io)。
Perfect是目前最流行的框架,这里也只介绍 Perfect。
1、Perfect简介
包含:完整强大工具箱、软件框架、Web应用服务器。
可运行平台:Linux、iOS和MacOS (OS X)。
可用于:开发Web应用和REST服务。
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服务器项目,下边介绍下这个项目。
[站外图片上传中...(image-8cf71d-1566791538242)]
打开PerfectTemplate.xcodeproj工程文件,编译。这时可能会报错(Xcode8.1后修复了)。
ld: library not found for -lCOpenSSL for architecture x86_64
解决该问题的方法就是设置 Library Search Paths 为 "$(PROJECT_DIR)/**"(包括双引号也)
3、Perfect目录
在Perfect Template项目模板中,有两个很重要的文件:
(1)Sources目录:包含了所有Perfect项目的Swift源程序文件。
(2)Package.swift:SPM文件管理清单,包含了整个项目对其它库函数的依存关系。这个文件可以理解为CocoaPod中的Podfile。
(3)webroot:存储相应的静态文件的根目录。可以在Source目录中的main.swift中对静态文件的根目录进行配置。
所有的SPM项目至少要包括一个 Sources 目录和一个 Package.swift 文件。
[站外图片上传中...(image-a0dcd7-1566791538242)]
在 Package.swift 文件中由两个重要的内容可能需要编辑。
第一个是 name 项目名称,用于说明当前项目的目标名称,因此最后可执行文件的名字也会按照这个名称进行编译。
第二个是 dependencies 依存关系清单。该内容说明了您的应用程序需要的所有子项目列表,在这个数组中其中每一个条目都包含了一个“.Package”软件包,及其来源URL和版本。
[站外图片上传中...(image-2bbd00-1566791538242)]
4、用 Perfect 写一个 RESTful API
功能:实现表单提交,返回Json数据。
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
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"}