2016-02-22 31 views
6

Kịch bản:Tải lên tệp nhanh Alamofire với yêu cầu đã ký: cách gửi tiêu đề ủy quyền?

  • iPhone iOS 8+ ứng dụng
  • dùng đăng nhập bằng sẽ tải lên một hình ảnh tiểu sử

Ứng dụng đã sử dụng Alamofire để đưa ra yêu cầu ký hợp đồng với các API backend. Thực sự đơn giản: ứng dụng gửi ba tiêu đề HTTP cụ thể (Authorization, X-Api-Keytimestamp) để yêu cầu được ký. Gọi số Alamofire.request thật dễ dàng để gửi headers làm thông số để nó hoạt động tốt đẹp.

Bây giờ, người dùng cần có thể tải lên ảnh tiểu sử của họ. Vì người dùng đã đăng nhập vào ứng dụng, API phụ trợ sẽ biết người dùng nào đang gửi hình ảnh theo số yêu cầu đã ký - và đó là phần khó khăn mà tôi đã đấu tranh trong vài giờ qua. Alamofire.upload chấp nhận các thông số hoàn toàn khác nhau từ .request, vì vậy tôi không thể tìm ra cách gửi tiêu đề khi tải lên tệp.

Đã thử số Alamofire.Manager.session.configuration.HTTPAdditionalHeaders cũ, nhưng đó là no longer supported. Tìm thấy các ví dụ về mã tải lên của tệp, không ai xem xét việc gửi tiêu đề tùy chỉnh.

Làm cách nào để gửi tiêu đề tùy chỉnh khi sử dụng phương thức 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) 
    }) 
} 

Yêu cầu đi qua. Trong nhật ký phụ trợ, tôi có thể thấy yêu cầu được trả lại HTTP 403 - Không được ủy quyền vì không thể ký yêu cầu. In các tiêu đề yêu cầu, không nhận được tiêu đề auth tùy chỉnh nào từ máy chủ.

+0

Thêm ít nhất là nguyên mẫu cho phương thức 'signRequest' –

+0

@DavidBerry được cập nhật. – mathielo

Trả lời

2

Trước khi bắt đầu, tôi muốn chia sẻ công cụ miễn phí (ứng dụng chrome) rất hữu ích trong loại tác phẩm này: DHC Rest Client: với công cụ này, bạn có thể xác minh xem thông số, tiêu đề và tệp tải lên có hoạt động không. muốn làm cho máy chủ.

Vì vậy, công việc này với Swift 2.xAlamofire 3.x:

Trước hết chuẩn bị tiêu đề của bạn:

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

Vì vậy, giả như bạn phải gửi một file zip và phản hồi sẽ là loại phản hồi TEXT/HTML (một chuỗi đơn giản với SUCCESS hoặc 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)") 
      } 

Thats tất cả nhưng nếu bạn muốn sử dụng multipartformdata bạn có thể làm điều đó thông qua tiêu đề của bạn thông qua tiêu đề điển với:

.upload (< # T ## phương pháp: Phương pháp ## Phương pháp #>, < # T # #URLString: URLStringConvertible ## URLStringConvertible #>, tiêu đề: < #T ## [Chuỗi: Chuỗi]?#>, MultipartFormData: < # T ## MultipartFormData -> Void #>

+0

Cảm ơn Alessandro! Tôi đã không nhận ra '.upload' có chữ ký bao gồm các tham số' headers', giải pháp của bạn hoạt động rất nhiều! Kết thúc bằng cách sử dụng 'multipartFormData', sẽ đăng mã cuối cùng của tôi để tham khảo trong tương lai. – mathielo

0

Sử dụng @alessandro-ornano 's câu trả lời tôi đã có thể thực hiện một upload ký yêu cầu sử dụng 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 
      } 
     }) 
} 
Các vấn đề liên quan