Zebedee
Zebedee

Reputation: 468

Post request with HTTP header parameters

I Want to use Bittrex api. I've read their api docs. There are explanations like the following.

For this version, we use a standard HMAC-SHA512 signing. Append apikey and nonce to your request and calculate the HMAC hash and include it under an apisign header.

$apikey='xxx';
$apisecret='xxx';
$nonce=time();
$uri='https://bittrex.com/api/v1.1/market/getopenorders?apikey='.$apikey.'&nonce='.$nonce;
$sign=hash_hmac('sha512',$uri,$apisecret);
$ch = curl_init($uri);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('apisign:'.$sign));
$execResult = curl_exec($ch);
$obj = json_decode($execResult);

I want to do this with Swift. But I don't want to use Alamofire. I wrote a code. I think I'm doing everything but I'm getting the following error.

{
    message = "APISIGN_NOT_PROVIDED";
    result = "<null>";
    success = 0;
}

I wrote similar code with Delphi. It works fine. So there is no problem with APIKEY. When I use the same parameters in Delphi, the same SecretHex is generated. So there's no problem with Encryption.

I think, I cannot do the Post Request with headers. I can not find the fault. Would you please help me.

func getBalances()
    {
        let apiKeySTR = "01235xxxxxx"
        let secretSTR = "41691xxxxxx"

        let path = "https://bittrex.com/api/v1.1/account/"
        let timeInterval = NSDate().timeIntervalSince1970
        let epochtime = String(floor(timeInterval))

        let urlFull = path + "getbalances" + "?" + "apikey=" + apiKeySTR + "&" + "nonce=" + epochtime

        let secretUInt8 : [UInt8] = Array(urlFull.utf8)
        var secretKey : [UInt8]?

        do {
            try secretKey = HMAC(key: secretSTR, variant: .sha512).authenticate(secretUInt8)
        } catch {
            print ("Error")
        }

        let secretHex = secretKey?.toHexString() ?? ""

        guard let url = URL(string: urlFull) else { return }
        var request = URLRequest(url: url)
        request.addValue("apising", forHTTPHeaderField: (secretHex))
        request.httpMethod = "POST"

        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) in
            if let response = response {
                print(response)
            }

            if let data = data {
                do {
                    let json = try JSONSerialization.jsonObject(with: data, options: [])
                    print(json)
                } catch {
                    print(error)
                }
            }

            }.resume()
    }

Upvotes: 3

Views: 5442

Answers (1)

Hexfire
Hexfire

Reputation: 6058

First off... you have a typo:

request.addValue("apising", forHTTPHeaderField: (secretHex))

I believe it's apisign, not "apising", right?


And below is a recap on creating REST API requests with a header and body. You can update this method according your needs:

1) Create URLRequest

    var request = URLRequest(url: requestURL)

2) Set headers and http method:

    request.allHTTPHeaderFields = ["Authentication" : "Bearer XYZ..."]
    request.httpMethod = "POST"

3) Set request body:

    // parameters is a simple [String:String] dictionary, just as header
    let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
    request.httpBody = jsonData

Complete example:

  public enum RESTMethod:String {
       case get = "GET"
       case post = "POST"
       case put = "PUT"
  }

  public func sendRequest(_ url: String,
                        method: RESTMethod,
                        headers: [String : String],
                        parameters: [String : Any],
                        completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionTask! {
    let requestURL: URL
    if method == .get {
        let parameterString = parameters.stringFromHttpParameters()
        requestURL = URL(string:"\(url)?\(parameterString)")!
    } else {
        requestURL = URL(string: url)!
    }


    var request = URLRequest(url: requestURL)
    request.allHTTPHeaderFields = headers
    request.httpMethod = method.rawValue
    if method == .post {
        let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
        request.httpBody = jsonData
    }
    request.timeoutInterval = 60


    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

        completionHandler(data,response,error)
    }

    task.resume()

    return task

}


extension Dictionary {

/// Build string representation of HTTP parameter dictionary of keys and objects

func stringFromHttpParameters() -> String {
    let parameterArray = self.map { (key, value) -> String in
        let percentEscapedKey = (key as! String).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
        let percentEscapedValue = (value as? String ?? "\(value)").addingPercentEncodingForURLQueryValue()!
        return "\(percentEscapedKey)=\(percentEscapedValue)"
    }

    return parameterArray.joined(separator: "&")
}

}

Usage:

sendRequest("http://yourserver",
             method: .get, // .post or .put
             headers: [],
             parameters: [],
             completionHandler: { (data, response, error) in

   // Handle response here

})

Upvotes: 3

Related Questions