1

I want to make a HTTP post request via Alamofire 4.5. The request need an authorization header(which is a Api key). But whenever I fired the request,my server cant detect the ApiKey.'

Here is how I make the Alamofire request

let params : [String : Any] =["param1":param1,"param2":param2]
let headers : HTTPHeaders = ["authorization" : apiKey]

Alamofire.request(MY_URL, method: .post, parameters: params, headers: headers).responseJSON {
     response in
     switch response.result{
     case .success(let result):

     //other code here
}

I triple checked the value of apiKey ,the value is correct,but the request sent,my server cant detect the authorization at all.

I totally no idea whether I do anything wrong here,cause I very new in Swift.Kindly provide a proper solution.Thanks

Edit :

In my server code,I using Slim 2

$app->map('/MY_URL','authenticate',function ()use($app){

}

'authenticate' is the point that scan for the authorization: apiKey in the headers,so now the problem is my server cant get the value of apiKey therefore always giving the same error "Api Key is missing" which I set when no Api Key found.

I tried the method below in Alamofire Documentation,but the result still the same.

What I tried:

 let headers: HTTPHeaders = [
    "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
    "Accept": "application/json"
]

Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}

What I missing here?Somebody please give me some hints to do it..Thank you.

EDIT:

To be more clear on my I mean for authorization : apiKey I show the way I make request in Postman.

Normally I just insert the "authorization": apiKey in the Headers in the request

enter image description here

but in Swift,the web service cant get the value of apiKey,therefore the server always return this following response :

{
  "error": true,
  "message": "Api key is missing"
}
ken
  • 2,426
  • 5
  • 43
  • 98
  • if you want to use __Basic HTTP Authentication__, then it is worth [read this](https://en.wikipedia.org/wiki/Basic_access_authentication). brief and it's on the spot. – holex Dec 08 '17 at 14:46
  • @holex nope,I just want to add the `authorization : API_KEY_HERE` in the headers on the request – ken Dec 08 '17 at 14:48
  • in that case you have not done anything wrong here. what is the problem? – holex Dec 08 '17 at 14:53
  • in my webservice cant get the value of `api_key` which I specific in `headers` of the request,how can this be?? – ken Dec 08 '17 at 15:00
  • you may need to share how you'd like to access to this header in your web-service...? – holex Dec 08 '17 at 15:06
  • I using Slim 2 in php,access header like so `$app->map('/myUrl','authenticate',function ()use($app){` (`authenticate` is the spot that detect `apiKey`)which is no problem for Android and Web version.But in Alamofire send the request,it cant detect the value of `apiKey` – ken Dec 08 '17 at 15:15
  • @holex I edited my question,can you get a look? – ken Dec 15 '17 at 06:13

3 Answers3

1

This is working fine for me with Alamofire 4.6.0

    let url = "WEB API URL"
    let headers = [
        "Content-Type":"application/x-www-form-urlencoded",
        "authorization" : "apiKey"
    ]

    let configuration = URLSessionConfiguration.default
    configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
    let params : [String : Any] = ["param1":param1,"param2":param2]

    Alamofire.request(url, method: .post, parameters: params as? Parameters, encoding: URLEncoding.httpBody, headers: headers).responseJSON { response in

        if let JSON = response.result.value {
            print("JSON: \(JSON)")

        }else{
            print("Request failed with error: ",response.result.error ?? "Description not available :(")

        }
    }
Rocky
  • 2,903
  • 1
  • 22
  • 26
  • can u give me a reason why we should define the configuration stuff??cause I just a newbie..so need to know about this – ken Dec 15 '17 at 06:41
  • this will specifies that the data for the URL load should be loaded from the originating source. No existing cache data should be used to satisfy a URL load request. – Rocky Dec 15 '17 at 06:46
  • But I just test your code,I still cant get the value of `apiKey` in my webservice..any idea? – ken Dec 15 '17 at 06:48
  • did you getting body parameters of request at server side? – Rocky Dec 15 '17 at 07:02
  • I not sure,but my webservice just return the result of "Api Key is missing" which I set myself is no ApiKey available..in another request which no need `apiKey` is running good..when need ApiKey,the value didnt get in my webservice.. – ken Dec 15 '17 at 07:15
  • ok, then test your API on [Postman](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en) so you will confirm on it. – Rocky Dec 15 '17 at 07:21
  • in postman my api return the value expected with the same value of `apiKey` which I use in swift,but in swift the result is `Api is missing` ,do you have any idea on this??Cause the headers seems never sent at all.. – ken Dec 17 '17 at 22:00
  • if it possible then share your URL & params, so i'll do it at my side. – Rocky Dec 18 '17 at 07:03
  • @ken with URL & param given above have same response 'Api is missing', in **Postman** also, if it working for you then export Postman request & share. – Rocky Dec 18 '17 at 08:37
  • is working..I edit my question now..U can try and see..I think your main reason is `authorization` you make it `Authorization` in the header – ken Dec 18 '17 at 09:00
0

Configuration is optional, the only thing you need is to setup request right. Make sure (double sure) that you format your auth correctly.

In some (not that rare cases this should be formatted like this:

["Authorization": "Bearer <#your_token#>"]

And what I found about Slim 2 it's also with Bearer so maybe you missing this. https://github.com/dyorg/slim-token-authentication/tree/master/example#making-authentication-via-header

Example from this:

$ curl -i http://localhost/slim-token-authentication/example/restrict -H "Authorization: Bearer usertokensecret"

With this, you can also check if this working with simple curl command. It should. If not, there is definitely a problem with fields you're sending rather than Alamofire itself.

In docs for Alamofire you can find:

    /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
    /// `method`, `parameters`, `encoding` and `headers`.
    ///
    /// - parameter url:        The URL.
    /// - parameter method:     The HTTP method. `.get` by default.
    /// - parameter parameters: The parameters. `nil` by default.
    /// - parameter encoding:   The parameter encoding. `URLEncoding.default` by default.
    /// - parameter headers:    The HTTP headers. `nil` by default.
    ///
    /// - returns: The created `DataRequest`.
public func request(_ url: URLConvertible, method: Alamofire.HTTPMethod = default, parameters: Parameters? = default, encoding: ParameterEncoding = default, headers: HTTPHeaders? = default) -> Alamofire.DataRequest

Here is an example:

Alamofire.request("https://...",
                   method: .get,
                   parameters: ["myKey1": "myValue1"], 
                   encoding: JSONEncoding.default,
                   headers: self.authHeader).responseJSON { response in 
                        //your response
                   }
Jakub
  • 13,712
  • 17
  • 82
  • 139
  • hey sir,so I need to form my authorization like this? ->>["authorization": "Bearer "] – ken Dec 15 '17 at 11:27
  • `self.authHeader` what it means in this part?I'm sorry I totally newbie,so have no idea on this... – ken Dec 15 '17 at 11:33
  • and I tried to modify my header `"authorization" : "Bearer <#apiKey#>"` like this..but I still didnt get the value of `apiKey` – ken Dec 15 '17 at 11:34
  • Sure, try with curl first. If it will work there we can adopt it to Alamofore. Follow those answers: https://stackoverflow.com/questions/356705/how-to-send-a-header-using-a-http-request-through-a-curl-call and if you made that work that will be easy to adopt it with swift – Jakub Dec 15 '17 at 11:37
  • No, command line tool. Just put it on terminal. This I posted before covers examples – Jakub Dec 15 '17 at 11:39
  • It’s called curl it’s build in in your system. Paste this in your terminal to see how it’s work: ‘curl -I http://google.com’ – Jakub Dec 15 '17 at 13:07
  • Terminal, just terminal.app, forgot about Xcode for a while ;) – Jakub Dec 15 '17 at 13:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/161302/discussion-between-ken-and-kuba). – ken Dec 15 '17 at 13:26
0

TLDR;

The problem is that iOS's URLRequest automatically capitalize headers. At the same time you API does not follow best practices.

Change your API to comply to RFC 7230 and allow it to accept headers case-insensitively.

The whole story:

At first, your question seemed a bit odd since there is no obviously wrong code in what you provided. Nevertheless I tried to reproduce your request in Postman.

Now we should stop and I must warn you to never post what you did in your "Here is my request" section. The information given there allowed me to completely reproduce your request in Postman (including headers and exact fields' names and values), which is good to solve your problem. But at the same time you shared your presumably private and maybe even bought API key to everyone who see your question. Which is obviously not good and I would recommend you to change your API key if it is possible.

Then I tried your code and noticed exactly the same behavior you talking about. I debugged responseJSON closure and observed response.request?.allHTTPHeaderFields property:

(lldb) po response.request?.allHTTPHeaderFields
▿ Optional<Dictionary<String, String>>
  ▿ some : 2 elements
    ▿ 0 : 2 elements
      - key : "Content-Type"
      - value : "application/x-www-form-urlencoded; charset=utf-8"
    ▿ 1 : 2 elements
      - key : "Authorization"
      - value : "f8f99f9506d14f0590863d5883aaac9b"

(if you don't understand what I wrote read about debugging in xcode and in particular for lldb's po command)

As you can see, authorization header's name start with a capital A letter even though I passed it all lowercased.

I tried to send new request with postman with capital A and yes - I learned that your API accepts only lower-cased authorization header name.

"It isn't really a problem" you think right now. "We should just change our authorization header name somewhere and it should be just fine, right?"

NOT SO EASY.

I tried a few things which all lead me to the URLRequest's setValue(_:forHTTPHeaderField:) method. Alamofire calls it and I tried it too. Surprisingly enough after calling this method "authorization" header always changes to "Authorization". Then I found the thing that particularly interesting for us:

Note that, in keeping with the HTTP RFC, HTTP header field names are case-insensitive.

Keep in mind that I even tried to change URLRequest's allHTTPHeaderFields directly. Had the same result.

Which leads us to the following conclusion: Apple intentionally ignores input headers' case and very irresponsibly changes it (again intentionally since it takes at least a few lines of code somewhere instead of just plugging given headers directly into request). As of now I know no possible solution to this problem (if we want to classify it as a problem which is a bit controversial). Search says that is exists from earlier days of iOS (http://0xced.blogspot.com.by/2010/06/fixing-nsmutableurlrequest.html). You could call some private objective-c APIs which could help, but in fact you'll get unstable or undefined behavior and would likely get rejected from App Store.

So my conclusion, and probably the only right choice in this situation is to change your API.

Fyodor Volchyok
  • 5,610
  • 4
  • 28
  • 45