一,Alamofire的说明与配置
(1)Alamofire 的前身是 AFNetworking。AFNetworking 是 iOS 和 OS X 上很受欢迎的第三方HTTP网络基础库。
(2)其实 AFNetwork 的前缀 AF 便是 Alamofire 的缩写。
(3)Swift发布后,AFNetworking的作者又用Swift语言写了个相同功能的库,这便是 Alamofire。
(4)Alamofire 本质是基于`NSURLSession`,并做了封装。使用 Alamofire 可以让我们网络请求相关代码(如获取数据,提交数据,上传文件,下载文件等)更加简洁易用。
(2)其实 AFNetwork 的前缀 AF 便是 Alamofire 的缩写。
(3)Swift发布后,AFNetworking的作者又用Swift语言写了个相同功能的库,这便是 Alamofire。
(4)Alamofire 本质是基于`NSURLSession`,并做了封装。使用 Alamofire 可以让我们网络请求相关代码(如获取数据,提交数据,上传文件,下载文件等)更加简洁易用。
关于Cookie:
Alamofire是基于NSURLRequest封装的,所以Cookie会自动保存,就和浏览器请求是一个效果。而且网站Set_cookie多久, 本地的Cookie就多久,每次请求的时候都会自动带上cookie,直到过期。(所以像登陆session这些的都不用我们手动去处理)
Alamofire是基于NSURLRequest封装的,所以Cookie会自动保存,就和浏览器请求是一个效果。而且网站Set_cookie多久, 本地的Cookie就多久,每次请求的时候都会自动带上cookie,直到过期。(所以像登陆session这些的都不用我们手动去处理)
2,Alamofire的功能特性:
(1)链式的请求/响应方法
(2)URL / JSON / plist参数编码
(3)上传类型支持:文件(File )、数据(Data )、流(Stream)以及MultipartFormData
(4)支持文件下载,下载支持断点续传
(5)支持使用NSURLCredential进行身份验证
(6)HTTP响应验证
(7)TLS Certificate and Public Key Pinning
(8)Progress Closure & NSProgress
3,Alamofire的安装与配置
(1)从 GitHub 上下载最新的代码:https://github.com/Alamofire/Alamofire
(2)将下载下来的源码包中 Alamofire.xcodeproj 拖拽至你的工程中
(3)工程 -> General -> Embedded Binaries项,把iOS版的framework添加进来: Alamofire.framework
(4)最后,在需要使用 Alamofire 的地方 import 进来就可以了
1
|
import Alamofire |
二,使用Alamofire进行数据请求
1,以GET请求为例
(1)不带参数,不带结果处理
1
|
|
(2)带参数,不带结果处理
1
|
|
(3)带参数,也带结果处理(这里以返回结果为json格式的为例)
1
2
3
4
5
6
7
8
9
10
11
|
.responseJSON { response in print (response.request) // original URL request print (response.response) // URL response print (response.data) // server data print (response.result) // result of response serialization if let JSON = response.result.value { print ( "JSON: (JSON)" ) //具体如何解析json内容可看下方“响应处理”部分 } } |
2,响应处理(Response Handling)
(1)除了上面样例使用的responseJSON(处理json类型的返回结果)外,Alamofire还提供了许多其他类型的响应处理方法:
response()
responseData()
responseString(encoding: NSStringEncoding)
responseJSON(options: NSJSONReadingOptions)
responsePropertyList(options: NSPropertyListReadOptions)
responseData()
responseString(encoding: NSStringEncoding)
responseJSON(options: NSJSONReadingOptions)
responsePropertyList(options: NSPropertyListReadOptions)
(2)Response Handler
1
2
3
4
5
6
7
|
.response { request, response, data, error in print (request) print (response) print (data) print (error) } |
1
2
3
4
5
6
|
.responseData { response in print (response.request) print (response.response) print (response.result) } |
1
2
3
4
5
|
.responseString { response in print ( "Success: (response.result.isSuccess)" ) print ( "Response String: (response.result.value)" ) } |
使用responseJSON 方法的话,JSON数据会被自动转化为 NSDictionary或NSArray。假设我们返回的json数据格式如下:
使用responseJSON自动解析son数据:
(6)同样也支持链式的返回结果处理
3,请求类型(HTTP Methods)
除了上面使用的 .Get 类型。Alamofire还定义了许多其他的HTTP 方法(HTTP Medthods)可以使用。
比如要使用 POST 请求,把 Alamofire.request 第一个参数做修改即可:
4,请求参数(Parameters)
(1)使用GET类型请求的时候,参数会自动拼接在url后面
(2)使用POST类型请求的时候,参数是放在在HTTP body里传递,url上看不到
5,参数编码方式(Parameter Encoding)
除了默认的方式外,Alamofire还支持URL、URLEncodedInURL、JSON、Property List以及自定义格式方式编码参数。
比如我们想要把一个字典类型的数据,使用json格式发起POST请求:
服务端php页面可以这么取得发送过来的JSON数据:
6,支持自定义Http头信息(HTTP Headers)
三,判断数据请求是否成功,并做相应的处理
在请求响应对象之前调用的 .validate() 函数是另一个易用的 Alamofire 特性。
将其与请求和响应链接,以确认响应的状态码在默认可接受的范围(200到299)内。如果认证失败,响应处理方法将出现一个相关错误,我们可以根据不同在完成处理方法中处理这个错误。
比如下面的样例,成功时会打印成功信息,失败时输出具体错误信息。
四,打印调试(print和debugPrint)
不管是 request对象还是 response对象都是支持打印输出的。根据不同的调试需求,我们可以自行选择使用 print 还是 debugPrint。
1,打印request对象
2,打印response对象
[
{
"name": "hangge",
"phones": [
{
"name": "公司",
"number": "123456"
},
{
"name": "家庭",
"number": "001"
}
]
},
{
"name": "big boss",
"phones": [
{
"name": "公司",
"number": "111111"
}
]
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.responseJSON { response in switch response.result { case . Success : //把得到的JSON数据转为数组 if let items = response.result.value as ? NSArray { //遍历数组得到每一个字典模型 for dict in items{ print (dict) } } case . Failure ( let error): print (error) } } |
1
2
3
4
5
6
7
|
.responseString { response in print ( "Response String: (response.result.value)" ) } .responseJSON { response in print ( "Response JSON: (response.result.value)" ) } |
除了上面使用的 .Get 类型。Alamofire还定义了许多其他的HTTP 方法(HTTP Medthods)可以使用。
1
2
3
|
public enum Method : String { case OPTIONS , GET , HEAD , POST , PUT , PATCH , DELETE , TRACE , CONNECT } |
1
|
|
4,请求参数(Parameters)
(1)使用GET类型请求的时候,参数会自动拼接在url后面
1
2
|
|
1
2
3
4
5
6
7
8
9
10
11
12
|
let parameters = [ "foo" : "bar" , "baz" : [ "a" , 1], "qux" : [ "x" : 1, "y" : 2, "z" : 3 ] ] // HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3 |
5,参数编码方式(Parameter Encoding)
除了默认的方式外,Alamofire还支持URL、URLEncodedInURL、JSON、Property List以及自定义格式方式编码参数。
1
2
3
4
5
6
7
8
9
10
|
enum ParameterEncoding { case URL case URLEncodedInURL case JSON case PropertyList (format: NSPropertyListFormat , options: NSPropertyListWriteOptions ) case Custom (( URLRequestConvertible , [ String : AnyObject ]?) -> ( NSMutableURLRequest , NSError ?)) func encode(request: NSURLRequest , parameters: [ String : AnyObject ]?) -> ( NSURLRequest , NSError ?) { ... } } |
1
2
3
4
5
6
7
8
9
|
let parameters = [ "foo" : [1,2,3], "bar" : [ "baz" : "qux" ] ] // HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}} |
1
2
3
4
5
6
7
8
|
<? $foo = $postdata [ "foo" ]; foreach ( $foo as $item ){ echo $item . "|" ; } //输出:1|2|3| |
6,支持自定义Http头信息(HTTP Headers)
1
2
3
4
5
6
7
8
9
|
let headers = [ "Authorization" : "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" , "Content-Type" : "application/x-www-form-urlencoded" ] .responseJSON { response in debugPrint(response) } |
三,判断数据请求是否成功,并做相应的处理
在请求响应对象之前调用的 .validate() 函数是另一个易用的 Alamofire 特性。
将其与请求和响应链接,以确认响应的状态码在默认可接受的范围(200到299)内。如果认证失败,响应处理方法将出现一个相关错误,我们可以根据不同在完成处理方法中处理这个错误。
比如下面的样例,成功时会打印成功信息,失败时输出具体错误信息。
1
2
3
4
5
6
7
8
9
10
|
.validate() .responseJSON { response in switch response.result { case . Success : print ( "数据获取成功!" ) case . Failure ( let error): print (error) } } |
四,打印调试(print和debugPrint)
不管是 request对象还是 response对象都是支持打印输出的。根据不同的调试需求,我们可以自行选择使用 print 还是 debugPrint。
1,打印request对象
1
2
3
4
5
6
|
print (request) /********** 下面是控制台输出 *************** ******************************************/ |
1
2
3
4
5
6
7
8
9
10
|
debugPrint(request) /********** 下面是控制台输出 *************** $ curl -i -H "User-Agent: hangge_970/com.hangge.hangge-970 (1; OS Version 9.1 (Build 13B137))" -H "Accept-Encoding: gzip;q=1.0,compress;q=0.5" -H "Accept-Language: zh-Hans-CN;q=1.0,en-CN;q=0.9" ******************************************/ |
2,打印response对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.responseString { response in debugPrint(response) } /********** 下面是控制台输出 *************** SUCCESS: { "args": {}, "headers": { "Accept": "*/ *", "Accept-Encoding" : "gzip;q=1.0,compress;q=0.5" , "Accept-Language" : "zh-Hans-CN;q=1.0,en-CN;q=0.9" , "Host" : "httpbin.org" , "User-Agent" : "hangge_970/com.hangge.hangge-970 (1; OS Version 9.1 (Build 13B137))" }, "origin" : "180.109.163.139" , } ******************************************/ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
.responseString { response in print (response) } /********** 下面是控制台输出 *************** [Request]: <NSMutableURLRequest: 0x7889c780> { URL: https://httpbin.org/get } [Response]: <NSHTTPURLResponse: 0x7896f500> { URL: https://httpbin.org/get } { status code: 200, headers { "Access-Control-Allow-Origin" = "*"; "Content-Length" = 354; "Content-Type" = "application/json"; Date = "Tue, 08 Dec 2015 01:57:45 GMT"; Server = nginx; "access-control-allow-credentials" = true; } } [Data]: 354 bytes [Result]: SUCCESS: { "args": {}, "headers": { "Accept": "*/ *", "Accept-Encoding" : "gzip;q=1.0,compress;q=0.5" , "Accept-Language" : "zh-Hans-CN;q=1.0,en-CN;q=0.9" , "Host" : "httpbin.org" , "User-Agent" : "hangge_970/com.hangge.hangge-970 (1; OS Version 9.1 (Build 13B137))" }, "origin" : "180.109.163.139" , } ******************************************/ |
六,使用Alamofire进行文件上传
File
Data
Stream
MultipartFormData
Data
Stream
MultipartFormData
2,使用文件流的形式上传文件
1
2
3
|
let fileURL = NSBundle .mainBundle(). URLForResource ( "hangge" , withExtension: "zip" ) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?php /** php 接收流文件 * @param String $file 接收后保存的文件名 * @return boolean */ function receiveStreamFile( $receiveFile ){ $streamData = isset( $GLOBALS [ 'HTTP_RAW_POST_DATA' ])? $GLOBALS [ 'HTTP_RAW_POST_DATA' ] : '' ; if ( empty ( $streamData )){ } if ( $streamData != '' ){ $ret = file_put_contents ( $receiveFile , $streamData , true); } else { $ret = false; } return $ret ; } //定义服务器存储路径和文件名 $receiveFile = $_SERVER [ "DOCUMENT_ROOT" ]. "/uploadFiles/hangge.zip" ; $ret = receiveStreamFile( $receiveFile ); echo json_encode( array ( 'success' =>(bool) $ret )); ?> |
3,上传时附带上传进度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
let fileURL = NSBundle .mainBundle(). URLForResource ( "hangge" , withExtension: "zip" ) .progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in print (totalBytesWritten) // This closure is NOT called on the main queue for performance // reasons. To update your ui, dispatch to the main queue. dispatch_async(dispatch_get_main_queue()) { print ( "Total bytes written on main queue: (totalBytesWritten)" ) } } .responseJSON { response in debugPrint(response) } |
4,上传MultipartFormData类型的文件数据(类似于网页上Form表单里的文件提交)
(1)上传两个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
let fileURL1 = NSBundle .mainBundle(). URLForResource ( "hangge" , withExtension: "png" ) let fileURL2 = NSBundle .mainBundle(). URLForResource ( "hangge" , withExtension: "zip" ) Alamofire .upload( . POST , multipartFormData: { multipartFormData in multipartFormData.appendBodyPart(fileURL: fileURL1!, name: "file1" ) multipartFormData.appendBodyPart(fileURL: fileURL2!, name: "file2" ) }, encodingCompletion: { encodingResult in switch encodingResult { case . Success ( let upload, _, _): upload.responseJSON { response in debugPrint(response) } case . Failure ( let encodingError): print (encodingError) } } ) |
1
2
3
4
5
6
7
|
<? move_uploaded_file( $_FILES [ "file1" ][ "tmp_name" ], $_SERVER [ "DOCUMENT_ROOT" ]. "/uploadFiles/" . $_FILES [ "file1" ][ "name" ]); move_uploaded_file( $_FILES [ "file2" ][ "tmp_name" ], $_SERVER [ "DOCUMENT_ROOT" ]. "/uploadFiles/" . $_FILES [ "file2" ][ "name" ]); ?> |
(2)文本参数与文件一起提交(文件除了可以使用fileURL,还可以上传NSData类型的文件数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
//字符串 let strData = "hangge.com" .dataUsingEncoding( NSUTF8StringEncoding ) //数字 let intData = String (10).dataUsingEncoding( NSUTF8StringEncoding ) //文件1 let path = NSBundle .mainBundle().pathForResource( "hangge" , ofType: "png" )! let file1Data = NSData (contentsOfFile: path) //文件2 let file2URL = NSBundle .mainBundle(). URLForResource ( "hangge" , withExtension: "zip" ) Alamofire .upload( . POST , multipartFormData: { multipartFormData in multipartFormData.appendBodyPart(data: strData!, name: "value1" ) multipartFormData.appendBodyPart(data: intData!, name: "value2" ) multipartFormData.appendBodyPart(data: file1Data!, name: "file1" , fileName: "h.png" , mimeType: "image/png" ) multipartFormData.appendBodyPart(fileURL: file2URL!, name: "file2" ) }, encodingCompletion: { encodingResult in switch encodingResult { case . Success ( let upload, _, _): upload.responseString { response in print (response) } case . Failure ( let encodingError): print (encodingError) } } ) |
1
2
3
4
5
6
7
8
9
10
|
<? $value1 = $_POST [ "value1" ]; $value2 = $_POST [ "value2" ]; move_uploaded_file( $_FILES [ "file1" ][ "tmp_name" ], $_SERVER [ "DOCUMENT_ROOT" ]. "/uploadFiles/" . $_FILES [ "file1" ][ "name" ]); move_uploaded_file( $_FILES [ "file2" ][ "tmp_name" ], $_SERVER [ "DOCUMENT_ROOT" ]. "/uploadFiles/" . $_FILES [ "file2" ][ "name" ]); ?> |
七,使用Alamofire进行文件下载
下面代码将logo图片下载下来保存到用户文档目录下(Documnets目录),文件名不变。
1
2
3
4
5
6
7
8
9
|
temporaryURL, response in let fileManager = NSFileManager .defaultManager() let directoryURL = fileManager. URLsForDirectory (. DocumentDirectory , inDomains: . UserDomainMask )[0] let pathComponent = response.suggestedFilename return directoryURL. URLByAppendingPathComponent (pathComponent!) } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
temporaryURL, response in let fileManager = NSFileManager .defaultManager() let directoryURL = fileManager. URLsForDirectory (. DocumentDirectory , inDomains: . UserDomainMask )[0] let folder = directoryURL. URLByAppendingPathComponent ( "file1" , isDirectory: true ) //判断文件夹是否存在,不存在则创建 let exist = fileManager.fileExistsAtPath(folder.path!) if !exist { try! fileManager.createDirectoryAtURL(folder, withIntermediateDirectories: true , attributes: nil ) } return folder. URLByAppendingPathComponent ( "myLogo.png" ) } |
2,使用默认提供的下载路径
Alamofire内置的许多常用的下载路径方便我们使用,简化代码。比如,下载到用户文档目录下可以改成:
1
2
3
4
5
|
let destination = Alamofire . Request .suggestedDownloadDestination( directory: . DocumentDirectory , domain: . UserDomainMask ) destination: destination) |
3,下载进度
下面代码在文件下载过程中会不断地打印下载进度,同时下载完成后也会打印完成信息。
1
2
3
4
5
6
7
8
9
10
11
|
let destination = Alamofire . Request .suggestedDownloadDestination( directory: . DocumentDirectory , domain: . UserDomainMask ) .progress { (bytesRead, totalBytesRead, totalBytesExpectedToRead) in let percent = totalBytesRead*100/totalBytesExpectedToRead print ( "已下载:(totalBytesRead) 当前进度:(percent)%" ) } .response { (request, response, _, error) in print (response) } |
4,断点续传(Resume Data)
当下载过程中被意外停止时,可以在响应方法中把已下载的部分保存起来,下次再从断点继续下载。
下面通过样例演示如何断点续传:
(1)程序启动后自动开始下载文件
(2)点击“停止下载”,终止下载并把已下载的数据保存起来,进度条停止走动。
(3)点击“继续下载”,从上次终止的地方继续下载,进度条继续走动。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
import UIKit import Alamofire class ViewController : UIViewController { //停止下载按钮 @IBOutlet weak var stopBtn: UIButton ! //继续下载按钮 @IBOutlet weak var continueBtn: UIButton ! //下载进度条 @IBOutlet weak var progress: UIProgressView ! //下载文件的保存路径 let destination = Alamofire . Request .suggestedDownloadDestination( directory: . DocumentDirectory , domain: . UserDomainMask ) //用于停止下载时,保存已下载的部分 var cancelledData: NSData ? //下载请求对象 var downloadRequest: Request ! override func viewDidLoad() { super .viewDidLoad() //页面加载完毕就自动开始下载 self .downloadRequest = Alamofire .download(. GET , destination: destination) self .downloadRequest.progress(downloadProgress) //下载进度 self .downloadRequest.response(completionHandler: downloadResponse) //下载停止响应 } //下载过程中改变进度条 func downloadProgress(bytesRead: Int64 , totalBytesRead: Int64 , totalBytesExpectedToRead: Int64 ) { let percent = Float (totalBytesRead)/ Float (totalBytesExpectedToRead) //进度条更新 dispatch_async(dispatch_get_main_queue(), { self .progress.setProgress(percent,animated: true ) }) print ( "当前进度:(percent*100)%" ) } //下载停止响应(不管成功或者失败) func downloadResponse(request: NSURLRequest ?, response: NSHTTPURLResponse ?, data: NSData ?, error: NSError ?) { if let error = error { if error.code == NSURLErrorCancelled { self .cancelledData = data //意外终止的话,把已下载的数据储存起来 } else { print ( "Failed to download file: (response) (error)" ) } } else { print ( "Successfully downloaded file: (response)" ) } } //停止按钮点击 @IBAction func stopBtnClick(sender: AnyObject ) { self .downloadRequest?.cancel() self .stopBtn.enabled = false self .continueBtn.enabled = true } //继续按钮点击 @IBAction func continueBtnClick(sender: AnyObject ) { if let cancelledData = self .cancelledData { self .downloadRequest = Alamofire .download(resumeData: cancelledData, destination: destination) self .downloadRequest.progress(downloadProgress) //下载进度 self .downloadRequest.response(completionHandler: downloadResponse) //下载停止响应 self .stopBtn.enabled = true self .continueBtn.enabled = false } } override func didReceiveMemoryWarning() { super .didReceiveMemoryWarning() } } |
八,使用Alamofire进行用户认证
比如:路由器的配置页面就常使用HTTP Basic认证。(浏览器输入路由器ip地址,如192.168.1.1,这时就会弹出个用户密码输入框进行权限验证。)
(2)BASE64 的加密强度非常低。当然,HTTP基本认证系统也可以与SSL或者Kerberos结合,实现安全性能较高(相对)的认证系统。
使用浏览器访问这个authenticate.php页面,则会弹出对话框要求输入用户名和密码:
如果输入正确的用户名(hangge)和密码(123)则返回正常的数据,否则返回错误信息并需要继续输入:
6,客户端代码
从控制台打印的消息可以看出,认证通过,并成功获取到数据:
1,Alamofire支持如下几种认证(Authentication)
HTTP Basic
HTTP Digest
Kerberos
NTLM
本文讲解使用 Alamofire 进行 HTTP Basic 验证。
HTTP Digest
Kerberos
NTLM
2,HTTP Basic认证介绍
(1)HTTP Basic认证是允许HTTP服务器对WEB浏览器进行用户身份证的方法。
(2)当一个客户端向HTTP服务器进行数据请求时,客户端会接收到HTTP服务器的身份认证要求,这时客户端会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密。并于每次请求数据时,将密文附加于请求头(Request Header)中。
(3)HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,
如果用户名及密码正确,则根据客户端请求,返回客户端所需要的数据。否则,返回错误代码或重新要求客户端提供用户名及密码。
3,HTTP Basic认证的使用场景
HTTP基本认证只提供简单的用户验证功能,优点是使用简单,适合于对安全性要求不高的系统或设备中。比如:路由器的配置页面就常使用HTTP Basic认证。(浏览器输入路由器ip地址,如192.168.1.1,这时就会弹出个用户密码输入框进行权限验证。)
4,HTTP Basic认证的缺点
(1)没有灵活可靠的认证策略,如无法提供域(domain或realm)认证功能。(2)BASE64 的加密强度非常低。当然,HTTP基本认证系统也可以与SSL或者Kerberos结合,实现安全性能较高(相对)的认证系统。
5,服务端代码
为了测试Alamofire的认证功能,我们首先要在服务端创建个带有认证的页面用于测试。这里以PHP为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?php //HTTP Basic认证 function authenticate() { header( 'WWW-Authenticate: Basic realm=""' ); header( 'HTTP/1.0 401 Unauthorized' ); echo "请输入正确的用户名和密码" ; exit ; } if (!isset( $_SERVER [ 'PHP_AUTH_USER' ]) || addslashes ( $_SERVER [ 'PHP_AUTH_USER' ])!= 'hangge' || addslashes ( $_SERVER [ 'PHP_AUTH_PW' ])!= '123' ) { //认证失败 authenticate(); } else { //认证成功 echo "欢迎您: {$_SERVER['PHP_AUTH_USER']}<br />" ; echo "当前时间:" . date ( 'h:i:s' ); //authenticate(); //重新开始 } ?> |
6,客户端代码
使用Alamofire进行认证:
1
2
3
4
5
6
7
8
9
|
let user = "hangge" let password = "123" .authenticate(user: user, password: password) .responseString { response in // debugPrint(response) print (response.result.value) } |