【问题标题】:Swift Alamofire file upload with signed request: how to send authorization headers?带有签名请求的 Swift Alamofire 文件上传:如何发送授权标头?
【发布时间】:2016-06-03 09:03:16
【问题描述】:

场景:

  • iPhone iOS 8+ 应用程序
  • 登录用户将上传个人资料图片

该应用已使用 Alamofire 向后端 API 发出签名请求。非常简单:应用程序发送三个特定的 HTTP 标头(AuthorizationX-Api-Keytimestamp)以供签名请求。调用Alamofire.request 很容易将headers 作为参数发送,所以它工作得很好。

现在用户需要能够上传他们的个人资料图片。由于用户已经登录到应用程序,后端 API 将通过 签名请求 知道哪个用户正在发送图片 - 这是过去几个小时我一直在努力解决的棘手部分。 Alamofire.upload 接受与.request 完全不同的参数,所以我不知道在上传文件时如何发送标头。

尝试了旧的Alamofire.Manager.session.configuration.HTTPAdditionalHeaders,但它是no longer supported。找到大量文件上传的代码示例,没有考虑发送自定义标头。

使用Alamofire.upload 方法时如何发送自定义标头?

typealias requestDataType = [String:AnyObject]
private func signRequest(data: requestDataType) -> [String:String] {
    var headers = [String:String]()

    var authString = ""
    var signatureHeaders = ""

    // Iterates over SORTED data dictionary to build headers
    for (k,v) in (data.sort{$0.0 < $1.0}) {
        if !authString.isEmpty {
            authString += "\n"
            signatureHeaders += " "
        }
        authString += "\(k): \(v)"
        signatureHeaders += "\(k)"
        headers[k] = "\(v)"
    }

    let userApiKey = _loggedInUser!["api_key"].string!
    let signature = authString.sha256(_loggedInUser!["api_secret"].string!)

    headers["X-Api-Key"] = userApiKey
    headers["Authorization"] = "Signature headers=\"\(signatureHeaders)\",keyId=\"\(userApiKey)\",algorithm=\"hmac-sha256\",signature=\"\(signature)\""

    return headers
}

func uploadProfilePicture(photo: UIImage, callback: apiCallback){
    guard let userId = _loggedInUser?["pk"].int else {
        callback(Response(success: false, responseMessage: "User not logged in"))
        return
    }

    let requestData: requestDataType = ["timestamp": "\(Int(NSDate().timeIntervalSince1970))"]

    let aManager = Manager.sharedInstance
    print(self.signRequest(requestData)) // Prints correct headers (Authorization, X-Api-Key, timestamp)
    aManager.session.configuration.HTTPAdditionalHeaders = self.signRequest(requestData)
    print(aManager.session.configuration.HTTPAdditionalHeaders) // Prints default headers, completely ignoring my custom headers

    aManager.upload(.POST, "\(_apiBaseUrl)profiles/\(userId)/photo/", multipartFormData: { multipartFormData in
        if let imageData = UIImageJPEGRepresentation(photo, 0.8) {
            multipartFormData.appendBodyPart(data: imageData, name: "upload", fileName: "userphoto.jpg", mimeType: "image/jpeg")
        }

        for (key, value) in requestData {
            multipartFormData.appendBodyPart(data: value.dataUsingEncoding(NSUTF8StringEncoding)!, name: key)
        }

        }, encodingCompletion: {
            encodingResult in

            debugPrint(encodingResult)
    })
}

请求通过。在后端日志中,我可以看到返回的请求 HTTP 403 - 未授权,因为无法签署请求。打印请求标头,服务器未收到自定义身份验证标头。

【问题讨论】:

  • 至少添加signRequest方法的原型
  • @DavidBerry 已更新。

标签: ios swift httprequest alamofire multipartform-data


【解决方案1】:

在初始化之前,我想分享一个在此类工作中非常有用的免费工具(chrome 应用程序):DHC Rest Client:使用此工具,您可以验证您的参数、标题和上传文件是否适用于请求类型你想制作到服务器。

所以,这适用于 Swift 2.xAlamofire 3.x

首先准备你的标题:

let headers = [
                "Content-Type": "application/zip",
                "X-Api-Key": userApiKey,
                ...whatever you need on headers..
            ]

因此,假设您必须发送一个 zip 文件,并且响应将是 TEXT/HTML 响应类型(带有 SUCCESS 或 ERROR 的简单字符串):

let filePath: String! = "/Users/admin.../Documents/myZipFile.zip"
var zipData: NSData! = NSData()
do {
    zipData = try NSData(contentsOfFile: filePath, options: NSDataReadingOptions.DataReadingMappedIfSafe)
} catch {
    print("- error during get nsdata from zip file\(error)")
}
let url :String! = String(format:"...myUrl?key1=%@&key2=%@",value1,value2)
Alamofire.upload(.POST, url, headers: headers, data: zipData)
                .responseString { response in
            if response.result.isSuccess {
                  let responseValue = response.result.value
                  print("Response value is: \(responseValue)")
            } else {
               var statusCode = 0
               if (response.response != nil) {
                  statusCode = (response.response?.statusCode)!
               }
               print("Error: \(response.result.error!) with statusCode: \(statusCode)")
            }

就是这样,但如果你想使用 multipartformdata,你可以通过 headers 字典传递你的标题:

.upload(, ,标题:, multipartFormData: Void#>

【讨论】:

  • 谢谢亚历山德罗!我没有意识到.upload 的签名包含headers 参数,您的解决方案非常有效!最终使用multipartFormData,将发布我的最终代码以供将来参考。
【解决方案2】:

使用@alessandro-ornano 的答案,我能够使用multipartFormData 发出上传签名请求:

func uploadProfilePicture(photo: UIImage, callback: apiCallback){
    guard let userId = _loggedInUser?["pk"].int else {
        callback(Response(success: false, responseMessage: "User not logged in"))
        return
    }

    let requestData: requestDataType = ["timestamp": "\(Int(NSDate().timeIntervalSince1970))"]
    let headers = self.signRequest(requestData)

    _alamofireManager
        .upload(.POST, "\(_apiBaseUrl)profiles/\(userId)/photo/", headers: headers, multipartFormData: { formData in
            if let imageData = UIImageJPEGRepresentation(photo, 1){
                formData.appendBodyPart(data: imageData, name: "upload", fileName: "userphoto.jpg", mimeType: "image/jpg")
            }
            for (k, v) in requestData {
                formData.appendBodyPart(data: v.dataUsingEncoding(NSUTF8StringEncoding)!, name: k)
            }
        }, encodingCompletion: { encodingResult in
            switch encodingResult {
            case .Success(let upload, _, _):
                upload.responseJSON { response in
                    self.responseHandler(response, callback: callback) // Class' private method
                }
            case .Failure(let encodingError):
                print(encodingError)
                self.dispatch_callback(callback, response: Response(success: false, responseMessage: "Unable to encode files for upload")) // Class' private method
            }
        })
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-01
    • 2020-01-06
    • 1970-01-01
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多