Swift在HTTP网络操作库Alamofire使用详解
(文章转载自:http://www.helloswift.com.cn/swiftmanual/2015/1212/3615.html)
1,什么是Alamofire
(2)其实 AFNetwork 的前缀 AF 便是 Alamofire 的缩写。
(3)Swift发布后,AFNetworking的作者又用Swift语言写了个相同功能的库,这便是 Alamofire。
(4)Alamofire 本质是基于`NSURLSession`,并做了封装。使用 Alamofire 可以让我们网络请求相关代码(如获取数据,提交数据,上传文件,下载文件等)更加简洁易用。
3,Alamofire的安装与配置
(1)从 GitHub 上下载最新的代码:https://github.com/Alamofire/Alamofire


|
1
|
import Alamofire
|
二,使用Alamofire进行数据请求
|
1
|
Alamofire.request(.GET, "https://httpbin.org/get")
|
|
1
|
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
|
|
1
2
3
4
5
6
7
8
9
10
11
|
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.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)")
}
}
|
2,响应处理(Response Handling)
(1)除了上面样例使用的responseJSON(处理json类型的返回结果)外,Alamofire还提供了许多其他类型的响应处理方法:
responseData()
responseString(encoding: NSStringEncoding)
responseJSON(options: NSJSONReadingOptions)
responsePropertyList(options: NSPropertyListReadOptions)
(2)Response Handler
|
1
2
3
4
5
6
7
|
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.response { request, response, data, error in
print(request)
print(response)
print(data)
print(error)
}
|
(3)Response Data Handler
|
1
2
3
4
5
6
|
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.responseData { response in
print(response.request)
print(response.response)
print(response.result)
}
|
(4)Response String Handler
|
1
2
3
4
5
|
Alamofire.request(.GET, "https://httpbin.org/get")
.responseString { response in
print("Success: \(response.result.isSuccess)")
print("Response String: \(response.result.value)")
}
|
(5)Response JSON Handler
|
1
2
3
4
|
Alamofire.request(.GET, "https://httpbin.org/get")
.responseJSON { response in
debugPrint(response)
}
|
(6)同样也支持链式的返回结果处理
|
1
2
3
4
5
6
7
|
Alamofire.request(.GET, "https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
}
|
3,请求类型(HTTP Methods)
除了上面使用的 .Get 类型。Alamofire还定义了许多其他的HTTP 方法(HTTP Medthods)可以使用。
|
1
2
3
|
public enum Method: String {
case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT
}
|
比如要使用 POST 请求,把 Alamofire.request 第一个参数做修改即可:
|
1
|
Alamofire.request(.POST, "http://httpbin.org/post")
|
4,请求参数(Parameters)
(1)使用GET类型请求的时候,参数会自动拼接在url后面
|
1
2
|
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
// https://httpbin.org/get?foo=bar
|
(2)使用POST类型请求的时候,参数是放在在HTTP body里传递,url上看不到
|
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
]
]
Alamofire.request(.POST, "https://httpbin.org/post", parameters: parameters)
// 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?)
{ ... }
}
|
比如我们想要把一个字典类型的数据,使用json格式发起POST请求:
|
1
2
3
4
5
6
7
8
9
|
let parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
]
]
Alamofire.request(.POST, "https://httpbin.org/post", parameters: parameters, encoding: .JSON)
// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
|
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"
]
Alamofire.request(.GET, "https://httpbin.org/get", headers: headers)
.responseJSON { response in
debugPrint(response)
}
|
三,判断数据请求是否成功,并做相应的处理
在请求响应对象之前调用的 .validate() 函数是另一个易用的 Alamofire 特性。
将其与请求和响应链接,以确认响应的状态码在默认可接受的范围(200到299)内。如果认证失败,响应处理方法将出现一个相关错误,我们可以根据不同在完成处理方法中处理这个错误。
比如下面的样例,成功时会打印成功信息,失败时输出具体错误信息。
|
1
2
3
4
5
6
7
8
9
10
|
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.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
|
let request = Alamofire.request(.GET, "https://httpbin.org/ip", parameters: ["foo": "bar"])
print(request)
/********** 下面是控制台输出 ***************
GET https://httpbin.org/ip?foo=bar
******************************************/
|
|
1
2
3
4
5
6
7
8
9
10
|
let request = Alamofire.request(.GET, "https://httpbin.org/ip", parameters: ["foo": "bar"])
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" \
"https://httpbin.org/ip?foo=bar"
******************************************/
|
2,打印response对象
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
Alamofire.request(.GET, "https://httpbin.org/get")
.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",
"url": "https://httpbin.org/get"
}
******************************************/
|
|
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
|
Alamofire.request(.GET, "https://httpbin.org/get")
.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",
"url": "https://httpbin.org/get"
}
******************************************/
|
1,Alamofire支持如下上传类型:
Data
Stream
MultipartFormData
2,使用文件流的形式上传文件
|
1
2
3
|
let fileURL = NSBundle.mainBundle().URLForResource("hangge", withExtension: "zip")
Alamofire.upload(.POST, "http://www.hangge.com/upload.php", file: fileURL!)
|
附:服务端代码(upload.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
/** php 接收流文件
* @param String $file 接收后保存的文件名
* @return boolean
*/
function receiveStreamFile($receiveFile){
$streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
if(empty($streamData)){
$streamData = file_get_contents('php://input');
}
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")
Alamofire.upload(.POST, "http://www.hangge.com/upload.php", file: fileURL!)
.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
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"]);
?>
|
1,自定义下载文件的保存目录
|
1
2
3
4
5
6
7
8
9
|
Alamofire.download(.GET, "http://www.hangge.com/blog/images/logo.png") {
temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory,
inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
return directoryURL.URLByAppendingPathComponent(pathComponent!)
}
|
2,使用默认提供的下载路径
Alamofire内置的许多常用的下载路径方便我们使用,简化代码。比如,下载到用户文档目录下可以改成:
|
1
2
3
4
5
|
let destination = Alamofire.Request.suggestedDownloadDestination(
directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, "http://www.hangge.com/blog/images/logo.png",
destination: destination)
|
3,下载进度
下面代码在文件下载过程中会不断地打印下载进度,同时下载完成后也会打印完成信息。
|
1
2
3
4
5
6
7
8
9
10
11
|
let destination = Alamofire.Request.suggestedDownloadDestination(
directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, "http://www.hangge.com/favicon.ico", destination: destination)
.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
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()
}
}
|
源码下载:
hangge_972.zip
八,使用Alamofire进行用户认证
HTTP Digest
Kerberos
NTLM
本文讲解使用 Alamofire 进行 HTTP Basic 验证。
(1)HTTP Basic认证是允许HTTP服务器对WEB浏览器进行用户身份证的方法。
(3)HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,如果用户名及密码正确,则根据客户端请求,返回客户端所需要的数据。否则,返回错误代码或重新要求客户端提供用户名及密码。
HTTP基本认证只提供简单的用户验证功能,优点是使用简单,适合于对安全性要求不高的系统或设备中。
比如:路由器的配置页面就常使用HTTP Basic认证。(浏览器输入路由器ip地址,如192.168.1.1,这时就会弹出个用户密码输入框进行权限验证。)
(1)没有灵活可靠的认证策略,如无法提供域(domain或realm)认证功能。
(2)BASE64 的加密强度非常低。当然,HTTP基本认证系统也可以与SSL或者Kerberos结合,实现安全性能较高(相对)的认证系统。
为了测试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(); //重新开始
}
?>
|
使用浏览器访问这个authenticate.php页面,则会弹出对话框要求输入用户名和密码:
如果输入正确的用户名(hangge)和密码(123)则返回正常的数据,否则返回错误信息并需要继续输入:
6,客户端代码
|
1
2
3
4
5
6
7
8
9
|
let user = "hangge"
let password = "123"
Alamofire.request(.GET, "http://www.hangge.com/authenticate.php")
.authenticate(user: user, password: password)
.responseString { response in
// debugPrint(response)
print(response.result.value)
}
|
从控制台打印的消息可以看出,认证通过,并成功获取到数据:







浙公网安备 33010602011771号