I have run into this problem and I cant seem to find any reasonable explanation nor solution.
Just recently I have migrated application from swift 2.3 to swift 3 and then to swift 4.
Today I have noticed that an API call that is returning HTTP status code 200 is interpreted as error in my iOS application. Example of API call:
self.clientApi.post(path: path,
parameters: params as [String : AnyObject]?,
encoding: Alamofire.URLEncoding(),
headers: nil,
success: { (result: ResultType) in
onSuccess()
}, failure: { (error, statusCode) in
onError(error, statusCode)
})
Using fiddler I can see that my API is returning status code 200 after this call, but onError() method is called and so on my application is thinking that request failed.
Error information that I have:
Type: NSError?
domain: "com.alamofirejsontoobjects.error
code: 1
What is not getting through my head is that I don't even use JSONEncoding here, I use URLEncoding because it is call to OAuth endpoint.
Please help.
After reinstalling all pods it has worked like a charm.
Thank you #Nitish
Related
I have implemented a working multipart upload on iOS using Alamofire. However from time to time an error occurs during the creation of the multipart upload. I would like to handle that error gracefully (currently it calls fatalError and crashes the whole app. I would rather like to do something like throwing and aborting the upload.
Some pseudo code as explanation.
Alamofire.upload(multipartFormData: {data in
if self.create() == .failure {
throw error
},
usingThreshold: UInt64.init(),
to: url,
method: .post,
headers: headers,
encodingCompletion: {error in
self.onEncodingComplete()}
)
Of course this throw does not work, since the closure is not throwing. Does anyone have a working solution for this?
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
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"
}
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 :(")
}
}
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.
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
}
I am trying to make a Alamofire POST request. This is how I am making the request..
Alamofire.request(url, method: .post, parameters: parameters, encoding: URLEncoding(destination: .queryString), headers : headers)
.responseString { response in
print(response.result)
}
Though I am getting the result as 'SUCCESS', the status code is always shown as 405 while it should have been 200. In the 'encoding' part of the request, I have tried everything like JSONEncoding.default,JSONEncoding.prettyPrinted, URLEncoding.httpbody...but always the status code is still 405. Can anyone please help? Thanks in advance...
This is the solution for this issue...A couple of changes had to be made..
The header which was given was this: let headers = [ "Content-Type" : "application/json"]. But it had to be let headers = [ "Content-Type":"application/x-www-form-urlencoded"].
Also the encoding should be given as URLEncoding.httpBody.
Making these changes made it work fine...
I think problem is with your server because this status code only comes when server disable the api
The HTTP 405 Method Not Allowed response status code indicates that
the request method is known by the server but has been disabled and
cannot be used. The two mandatory methods, GET and HEAD, must never be
disabled and should not return this error code.
So Contact your server(backend developer), make sure your url is correct
I have faced same problem. The API endpoint worked fine with Android Retrofit, also tested with PostMan. Also the header's Content-Type was application/json.
It was really strange bug. I've used Fiddler to check the response.
The error message was
The requested resource does not support http method 'T'/'ST'.
I used GET/POST method, but it said I was using the T/ST, instead of GET/POST
I found answer from the Alamofire's issues.
When I called the API endpoint without parameters, I used the blank dictionary as parameter.
Alamofire.request("URL",
method: .get,
parameters: [:],
encoding: JSONEncoding.default).responseString(completionHandler: completionHandler)
I changed it to nil
Alamofire.request("URL",
method: .get,
parameters: nil,
encoding: JSONEncoding.default).responseString(completionHandler: completionHandler)
After that, it worked fine.
Hope it will help you. Cheers!
Try to replace:
responseString with responseJSON
AND URLEncoding(destination: .queryString) with URLEncoding.default
LIKE:
Alamofire.request(strURL, method: .post, parameters: params, encoding: URLEncoding.default, headers: headers).responseJSON { (responseObject) -> Void in
//Do something
}
Error callback does not work if async request is sent but success does! and it works perfectly fine in Android and browser.
In iPhone it works for synchronous request. here is my code.
other apis work perfectly fine.
$.ajax({
type: 'POST',
url: "https://api.cloud.appcelerator.com/v1/users/login.json?key=xxxxxxxxx",
data: {
"login": useremail,
"password": password
},
success: function (resp) {
console.log(resp);
console.log('User logged-in successfully');
},
error: function (e) {
console.log(e)
}
});
API returns status code 200 for correct email and password but 401 for incorrect one so if status code is 200 its works well I get response in success.
This seems to be a very common issue with Cordova + iOS + jQuery combo.
Seems like there are few ways to resolve this 401 error response handling issue. One is to add timeout time out attribute while making AJAX request and handling the error. The other approach is to handle it in the server side by sending request over HTTPS and returning back authentication token with error details in case of 401 error.
Request you to have a look at this post for more info.
Also currently you cannot differentiate these two errors (401 and 408 errors) in iOS as i could see this defect still open in official Apache Cordova Bug Tracking System.Request you to check out this bug
I have the following error after upgrading Alamofire to version 2.0.0.0 using Swift 2
Anyone know a fix for this?
The Request Serialisation, i.e. responseJson changed signatures in AlamoFire 2.0. The Migration Guide explains in full, but the abbreviated version is as follows.
You now need to supply a closure which takes a request, a response, and a result. The result is an enum, which can either be .Success(Value) or .Failure(NSData?, ErrorType).
So your code should look something like this:
Alamofire.request(.GET, urlString, parameters: params, encoding: .URL)
.responseJson(options: .MutableContainers) { (request, response, result) -> Void in
print(result)
}