I'm using Alamofire to get data from my server. However, it doesn't catch the error, as the error returned is nil. I've tested with AFNetworking, and it works fine. For both operation, the status code returned is 401 Unauthorized . Is there's something with my code?
I'm using GrapeAPI for my backend. All it does is just to return the error on fail request
GrapeAPI
error!('Unauthorized', 401)
AFNetworking
manager.GET("someUrl", parameters: nil, success: { (_, object) in
}, failure: { (operation, error) in
// These are the outputs. I'm not assigning any values
// error.localizedDescription = "Request failed: unauthorized (401)"
// statusCode = 401
})
Alamofire
Alamofire.request(.GET, "url", parameters: nil)
.response { (a,b,data,error) in
// These are the outputs. I'm not assigning any values
// error = nil
// data = {"error":"Unauthorized"}
// statusCode = 401
}
I can check the failure using the statusCode. But I prefer to check the error object instead. However, since the error is nil in Alamofire, it's quite confusing to check whether the request has failed or not.
As Matt has mentioned in the comment, I need to add .validate() before calling .response(). This is by design. Final code as below:
Alamofire.request(.GET, "url", parameters: nil)
.validate()
.response { (a,b,data,error) in
// error won't be nil now
// and statusCode will be 401
}
Read this detailed explanation(thanks!) for more information.
Alamofire does not see 401 Unauthorized as an error as it is an valid return. In your comment code you are assigning a value to error not checking it for error, it should be:
Alamofire.request(.GET, "url", parameters: nil)
.response { (a,b,data,error) in
if error != nil{
println(error.localizedDescription)
} else {
if let data = data{
//You should probably use a switch statement here
if data.statusCode == 401 {
println("Unauthorized")
} else if data.statusCode == 200 {
println("Success")
}
}
}
I am not sure if i understand correctly your problem, but I hope that help!
Related
I am making a request to a server using Alamofire. Here is how i am doing it:
Alamofire.request(url, method: .post, parameters: [:] ,encoding: JSONEncoding.default).responseJSON { response in
print("response=\(response)")
print("Response=:\((response.response?.statusCode)!)")
switch response.result{
case .success :
let passList = AuthenticateSuccess(nibName: "AuthenticateSuccess", bundle: nil)
self.navigationController?.pushViewController(passList, animated: true)
print("connected")
case .failure(let error):
self.showAlertTost("", msg: "Authentication Failed. Authenticate again!", Controller: self)
}
}
This is what prints:
response=SUCCESS: {
message = "Access denied.";
}
Response=:401
connected
I want to know that if 401 is error why is success block being executed? Is failure case in Alamofire handled differently?
As the documentation says:
By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling validate() before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type.
E.g.
Alamofire.request(url, method: .post, encoding: JSONEncoding.default)
.validate()
.responseJSON { response in
...
}
With validate, non 2xx responses will now be treated as errors.
response.success depicts that the server has returned the response. Whereas 401 is something that is related to the REST response which your backend system generated. Hence add the check to response code after verifying that you have received the response to provide better information to the end-user.
i am using alamofire to call api. but in this particular api i am getting nil value as a response. actually when i hit my api in my browser its working pretty well.
here is my code
func web()
{
request(.GET, "http://www.horecasupply.nl/AJAX?function=appStructure", parameters: nil, encoding: .JSON).responseJSON { (response : Response<AnyObject, NSError>) -> Void in
if response.result.value != nil {
print(response.result.value)
}
}
}
getting nil value and when i print respose its showing me
Printing description of response.result.Failure:
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 1."
UserInfo={NSDebugDescription=Invalid value around character 1.}
EDIT 1:: tried responsestring instead of responsejson.
but getting different kind of response so how can i convert it?
\r\n\r\n\r\n\r\n</script>\r\n DD_belatedPNG.fix(\'img, .png_bg\'); //fix any or .png_bg background-images </script>\r\n\r\n\r\n</script>\r\n</script>-->\r\n\r\n\r\n\r\n\r\n\r\n \r\n\r\n")
Give this code a shot, this should work:
Alamofire.request(.GET, "http://www.horecasupply.nl/AJAX?function=appStructure").responseJSON { response in
if response.result.value != nil {
print(response.result.value)
}
}
I'm using Alamofire with Swift kind of this way:
Alamofire.request(.GET, urlString)
.authenticate(usingCredential: credential)
.response {
(request, responseJSON, data, error) in [..and so on]
Now I wonder how I can execute some code in case the server is e.g. completely down. Something like a failed block in ObjC.
I know that I can call something like this to get an error code:
if let response = responseJSON {
var statusCode = response.statusCode
println("-->statusCode: \(statusCode)")
}
But in the case that I can't reach the server, the .response closure won't execute, so there is no error message.
How is this handled?
If you can't reach the server, you will receive a NSURLErrorDomain error via the error variable in the closure.
I have the HTTP code in an AngularJS controller:
$http.post('/api/users/authenticate', {email: $scope.email, password: $scope.password})
.success(function (data, status, headers, config) {
authService.login($scope.email);
$state.go('home');
})
.error(function (data, status, headers, config) {
$scope.errorMessages = data;
$scope.password = "";
});
In the success case, the server will respond with a JSON representation of a user. In the error case the server will respond with a simple string such as User not found which can be accessed through the data parameter.
I'm having trouble figuring out how to do something similar in Alamofire. Here's what I have right now:
#IBAction func LoginPressed(sender: AnyObject) {
let params: Dictionary<String,AnyObject> = ["email": emailField.text, "password": passwordField.text]
Alamofire.request(.POST, "http://localhost:3000/api/users/authenticate", parameters: params)
.responseJSON {(request, response, data, error) in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {
let welcome = self.storyboard?.instantiateViewControllerWithIdentifier("login") as UINavigationController;
self.presentViewController(welcome, animated: true, completion: nil);
})
}
else{
dispatch_async(dispatch_get_main_queue(), {
// I want to set the error label to the simple message which I know the server will return
self.errorLabel.text = "something went wrong"
});
}
}
}
I have no idea if I'm handling the non-error case correctly either and would appreciate input on that as well.
You are are on the right track, but you are going to run into some crucial issues with your current implementation. There are some low level Alamofire things that are going to trip you up that I want to help you out with. Here's an alternative version of your code sample that will be much more effective.
#IBAction func loginPressed(sender: AnyObject) {
let params: [String: AnyObject] = ["email": emailField.text, "password": passwordField.text]
let request = Alamofire.request(.POST, "http://localhost:3000/api/users/authenticate", parameters: params)
request.validate()
request.response { [weak self] request, response, data, error in
if let strongSelf = self {
let data = data as? NSData
if data == nil {
println("Why didn't I get any data back?")
strongSelf.errorLabel.text = "something went wrong"
return
} else if let error = error {
let resultText = NSString(data: data!, encoding: NSUTF8StringEncoding)
println(resultText)
strongSelf.errorLabel.text = "something went wrong"
return
}
var serializationError: NSError?
if let json: AnyObject = NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments, error: &serializationError) {
println("JSON: \(json)")
let welcome = self.storyboard?.instantiateViewControllerWithIdentifier("login") as UINavigationController
self.presentViewController(welcome, animated: true, completion: nil)
} else {
println("Failed to serialize json: \(serializationError)")
}
}
}
}
Validation
First off, the validate function on the request will validate the following:
HTTPStatusCode - Has to be 200...299
Content-Type - This header in the response must match the Accept header in the original request
You can find more information about the validation in Alamofire in the README.
Weakify / Strongify
Make sure to weak self and strong self your closure to make sure you don't end up creating a retain cycle.
Dispatch to Main Queue
Your dispatch calls back to the main queue are not necessary. Alamofire guarantees that your completion handler in the response and responseJSON serializers is called on the main queue already. You can actually provide your own dispatch queue to run the serializers on if you wish, but neither your solution or mine are currently doing so making the dispatch calls to the main queue completely unnecessary.
Response Serializer
In your particular case, you don't actually want to use the responseJSON serializer. If you do, you won't end up getting any data back if you don't pass validation. The reason is that the response from the JSON serialization is what will be returned as the AnyObject. If serialization fails, the AnyObject will be nil and you won't be able to read out the data.
Instead, use the response serializer and try to parse the data manually with NSJSONSerialization. If that fails, then you can rely on the good ole NSString(data:encoding:) method to print out the data.
Hopefully this helps shed some light on some fairly complicated ways to get tripped up.
So Alamofire treats all requests successful. This really comes down to the API server http headers being returned.
You could use Alamofire.Request.validate()
It'll allow you to validate http headers, etc. Check out the example
https://github.com/Alamofire/Alamofire#validation
I am assuming the the error message will be in the data object.
to access the values from data you could do something like
I am not really sure about your api response looks but in this example
{
"message": "Could not authenticate"
}
let message: String? = data?.valueForKey("message") as String
I tried a long time to handle a JSON response with SwiftyJSON, but I don't know how to parse the response. Here's my code:
var jsonString:String = ""
Alamofire
.request(.GET, url + "/HMServer/rest/administration/version")
.responseJSON {
(request, response, data, error) -> Void in
let json = JSON(object: data!)
//here I want to do something with parsing
}
The requests I do with Alamofire and get back a JSON response. The response look like:
[message: [SERVER_VERSION: 0.1, INTERFACE_VERSION: 0.1], type: success]
I want to save all elements in strings and give them back. How can I parse the JSON response saved in the let let json? I tried to use Alamofire-SwiftJSON but the code does not work. All examples I found are too old because the SwiftyJSON code was refactored a few days ago.
THX!
I have fixed Alamofire-SwiftJSON's issue,
but you can do it by yourself in responseJSON's closure like:
Alamofire.request(.GET, url + "/HMServer/rest/administration/version")
.responseJSON { (request, response, data, error) -> Void in
if error != nil {
self.swiftyJSON = SwiftyJSON.JSON.Null(error)
} else if object != nil {
self.swiftyJSON = SwiftyJSON.JSON(object: object!)
} else {
self.swiftyJSON = SwiftyJSON.JSON.Null(nil)
}
}
Above code is not like Alamofire-SwiftJSON in the global queue, the initialization (AnyObject to SwiftyJSON) is running in the main queue.