I receive some JSON that consists of results, response and if a query is successful, a value for success of 1. If a query is not successful, there is no value in the JSON for success set. What I would like to do is test for the value of success of 1, however, my code to detect this is throwing an exception.
NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSDictionary *jsonresponse = jsonResults[#"response"];
if (![jsonResults isKindOfClass:[NSNull class]]
&&![jsonResults[#"response"]isKindOfClass:[NSNull class]]
&&![jsonResults[#"response"][#"success"]isKindOfClass:[NSNull class]]){
//CODE BREAKS BEFORE GETTING HERE
NSLog(#"got here");
This is what the JSON on fail looks like when logged to console:
{
code = 400;
error = "0(NSNull)";
response = "Bad Request-No id found";
}
How would I fashion a test for a value set of success that would not throw an exception upon receiving the above?
Thanks in advance for any suggestions.
Rather than checking the error string or response, you should really check the reply code instead (ie 400, 200, etc). A list of standard codes are available here: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
Related
In my project I need to send JSON object in web service API call. I have converted JSON from array.
do {
let theJSONData = try NSJSONSerialization.dataWithJSONObject(
param ,
options: NSJSONWritingOptions(rawValue: 0))
var theJSONText : String = String(data: theJSONData,
encoding: NSASCIIStringEncoding)!
print(theJSONText)
theJSONText = theJSONText.stringByReplacingOccurrencesOfString("\\", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
print(theJSONText)
let newParam = ["ESignData":theJSONText]
} catch let error as NSError {
print(error)
}
it print string correctly as
{"EntNum":"47","JobNo":"1737753","ClientID":"100","HospNo":"1","QAReason":"","DoctorNo":"1694","Action":"Sign"}
{"EntNum":"47","JobNo":"1737753","ClientID":"100","HospNo":"1","QAReason":"","DoctorNo":"1694","Action":"Sign"}
Now When I try to send this newParam dictionary in API call, it contains "\" in string parameters of JSON string.
WebService.PostURL(mainLink, methodname: ESIGNTHISDOC, param: newParam, userName: AUTH_USERNAME, password: AUTH_PWD, CompletionHandler: { (success, response) in
})
And in that web service method I have print param.
Param = {
ESignData = "{\"EntNum\":\"47\",\"JobNo\":\"1737753\",\"ClientID\":\"100\",\"HospNo\":\"1\",\"QAReason\":\"\",\"DoctorNo\":\"1694\",\"Action\":\"Sign\"}";
}
Now in this I know it is obvious in iOS because of " in string. Now the problem is that there are lots of APIs working in android app, and the API developer doesn't want to update his code according to us.
I know this problem happens because of adding JSON string in dictionary as parameter. But I have not proper justification for that so if any proof will be also helpful for me to convince him.
Any solution to convert the JSON string without backslash in iOS? I need to fix from my side if possible. Any help will be appreciate.
EDIT :
On server side it needs like
ESignData = {"EntNum":"47","JobNo":"1737753","ClientID":"100","HospNo":"1","QAReason":"","DoctorNo":"1694","Action":"Sign"}
If I pass this as parameter in POSTMAN than it gives success message. But not with our object with "\" in it.
EDIT 2:
Now printing the newParam dictionary:
print(newParam)
print("-------------------------")
print(newParam["ESignData"])
And logs :
["ESignData": "{\"EntNum\":\"47\",\"JobNo\":\"1737754\",\"ClientID\":\"100\",\"HospNo\":\"1\",\"QAReason\":\"\",\"DoctorNo\":\"1694\",\"Action\":\"Sign\"}"]
-------------------------
Optional("{\"EntNum\":\"47\",\"JobNo\":\"1737754\",\"ClientID\":\"100\",\"HospNo\":\"1\",\"QAReason\":\"\",\"DoctorNo\":\"1694\",\"Action\":\"Sign\"}")
And by debug :
Printing description of newParam:
▿ 1 elements
▿ [0] : 2 elements
- .0 : "ESignData"
- .1 : "{\"EntNum\":\"47\",\"JobNo\":\"1737754\",\"ClientID\":\"100\",\"HospNo\":\"1\",\"QAReason\":\"\",\"DoctorNo\":\"1694\",\"Action\":\"Sign\"}"
So it shows that it is in our dictionary. All the " are joined by \.
I ran into this exact issue today. For me it appears that the default encoding for any NSURLRequest is a string. So, somewhere between my creating the dictionary request and the server parsing it, the backslashes would appear and the server had problems with my payload.
I solved the issue by explicitly stating that my payload was JSON by setting the content type header.
[authRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
Now when I create JSON data from a dictionary, the backslashes don't appear and the server is able to parse everything correctly.
Code snippet below for completeness:
NSMutableURLRequest *authRequest = [[[NSURLRequest alloc] initWithURL:authURL] mutableCopy];
[authRequest setHTTPMethod:#"POST"];
NSURLSession *session = [NSURLSession sharedSession];
[authRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSDictionary *bodyDictionary = #{#"User_Name": user, #"Password_Hash": password};
if ([NSJSONSerialization isValidJSONObject:bodyDictionary]) {
NSError *error;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:bodyDictionary options:0 error:&error];
if (!error) {
[authRequest setHTTPBody:bodyData];
} else {
NSLog(#"Unable to convert to JSON DATA %#", error.localizedDescription);
}
}
I am getting problem while encryption.
The server is sending json data which is aes256 encrypted and then base64 encoded.
while in the ios client side i am able to get the response and decode it using base64.
The AES256 decryption works on some libraries(3rd party or wrappers aroound CommonCryptor.h) and not working in another.
When decryption is working the parsing is not working.
The following are the wrappers libraries and the respective code.
RNCryptor
(https://github.com/rnapier/RNCryptor)
NSData *decodedData = [Util decode:data];
NSData *RNDecryptedData = [RNDecryptor decryptData:decodedData withPassword:randomString error:&error];
if (error == nil) {
NSLog(#"RNDecryptedData - %#",[Util hexStringFromData:RNDecryptedData]);
response = [NSJSONSerialization JSONObjectWithData:RNDecryptedData options:NSJSONReadingMutableContainers error:&error];
NSLog(#"response - %#",response);
NSLog(#"error1 - %#",error);
} else
NSLog(#"error2 - %#",error);
I am getting following error while decryption.
EncryptedParsing[4402:70b] error2 - Error Domain=net.robnapier.RNCryptManager Code=2 "Unknown header" UserInfo=0x8c6bd60 {NSLocalizedDescription=Unknown header}
CCrypto
(https://github.com/Gurpartap/AESCrypt-ObjC)
NSData *decodedData = [Util decode:data];
NSData *CCDecryptedData = [decodedData decryptedAES256DataUsingKey:randomString error:&error];
if (error == nil) {
response = [NSJSONSerialization JSONObjectWithData:CCDecryptedData options:NSJSONReadingMutableContainers error:&error];
NSLog(#"response - %#",response);
NSLog(#"error1 - %#",error);
} else
NSLog(#"error2 - %#",error);
Here I am getting the decrypted data, but while parsing it is giving following error
EncryptedParsing[4469:70b] error1 - Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x8a51520 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
NSData+AES256
(http://pastie.org/426530)
NSData *decodedData = [Util decode:data];
NSData *AES256DecryptedData = [decodedData AES256DecryptWithKey:randomString];
response = [NSJSONSerialization JSONObjectWithData:AES256DecryptedData options:NSJSONReadingMutableLeaves error:&error];
NSLog(#"error - %#",error);
I am getting the decryption data, while parsing i am getting following error
EncryptedParsing[4646:70b] error - Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x8a710c0 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
Along with these I have also used CocoaSecurity (https://github.com/kelp404/CocoaSecurity)
But it is not working.
I am using NSData+Base64 (https://github.com/l4u/NSData-Base64) for base64 decoding
By the way there is no problem from server side(we tested it).
I want to know the error i am doing. Or is there any other way to achieve it
There is no universal standard way to encode AES encrypted data. You need agreement on both sides about how to encode the various metadata required.
RNCryptor is failing to decrypt because it expects the data to be in the RNCryptor format.
CCCrypto and AES256DecryptWithKey are probably interpreting the key differently than you intend. Neither verifies that the key you pass is correct, so you likely are getting garbage back. If that's the case, I would expect that for some messages you get garbage back, and for other messages you get nothing back, or an error.
RNCryptor has implementations in several languages if you need a cross-platform solution. If you are required to conform to a server format (which is likely not secure encryption from the looks of your code), then you'll need to provide the server code in order to determine the correct client code.
try to replace NSJSONReadingMutableLeaves with kNilOptions.
I have piece of good working code that downloads and parses JSON from server and returns result in +JSONRequestOperationWithRequest:success:failure: of AFJSONRequestOperation. success (and also failure)part of method takes block as an argument with id JSON argument in it. If I understand right - this object is for representation of JSON file or it's content. I need to have a possibility to send this JSON file (or it's text representation in every other text format) to email, using MFMailComposeViewController. So, two questions:
how can I get this JSON content from this file?
do I need register .json filetype to have possibility to send it using MFMailComposeViewController?
how can I get this JSON content from this file?
You can get the JSON data response (before it's converted to Foundation objects by NSJSONSerialization) from
operation.responseString (as NSString), or
operation.responseData (as NSData).
You'll want the latter if you're going to attach it to an e-mail.
do I need register .json filetype to have possibility to send it using
MFMailComposeViewController?
You need to call [MFMailComposeViewController -addAttachmentData:mimeType:fileName:]. You can get the mime-type from the operation.response.allHeaderFields NSDictionary. You can also use the official standard, application/json, or text/json, which is commonly used as well.
The response in the succes if not JSON but the object representation of the JSON. Most likely a NSArray or NSDictionary.
What you need to do is either use a normal HTTP request and send that string to the MFMailComposeViewController or change the JSON object back into a JSON string:
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:&error];
NSString *jsonString = nil;
if (jsonData) {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
} else {
NSLog(#"Error creating JSON: %#", error);
}
Then you can pass the jsonString in your MFMailComposeViewController.
This is my bit of code doing a GET request to a REST api.
Im not sure how to get back the message if I get an error:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSURL *URL = [NSURL URLWithString:urlString];
[request setURL:URL];
[request setHTTPMethod:#"GET"];
NSError *err = nil;
NSHTTPURLResponse *res = nil;
NSData *retData = [NSURLConnection sendSynchronousRequest:request returningResponse:&res error:&err];
if (err) // This part is never called.
{
NSLog(#"Error: %#", err);
}
else
{
if (res.statusCode != 200)
{
// show the user the status message
NSLog(#"Error: %#", res); // This part is called
}
else
{
}
}
I want to get the error message if it was not successful. But the if (err) block is never called. err is still null, although the statuscode is 400.
And if successful I will get back a json response.
In the code above I get back a statusCode of 400
The error block is not called because the error object is created only if a system level error occurs. This does not happen because the request is sent correctly and the server sends a response. If you are in control of the server, you should probably make it return status code 200 and include an app level status code in the response, that would tell your app that the entered credentials are incorrect.
Edit:
To get status message you can use
+ (NSString *)localizedStringForStatusCode:(NSInteger)statusCode
This is a class method of the NSHTTPURLResponse class.
if (res.statusCode != 200)
{
// show the user the status message
NSLog(#"Error: %#", [NSHTTPURLResponse localizedStringForStatusCode: res.statusCode]); // This part is called
}
Take a look at the NSError class reference:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/Reference/Reference.html
You can try to log the error message from the localizedDescription.
you are receiving this status code because- The Web server (running the Web site) thinks that the data stream sent by the client (e.g. your Web browser or our CheckUpDown robot) was 'malformed' i.e. did not respect the HTTP protocol completely. So the Web server was unable to understand the request and process it
to log above problem in respect to ios visit this link
If you read the documentation of sendSynchronousRequest...
error
Out parameter used if an error occurs while processing the request. May be NULL.
this mean that erro will be a valid NSError object in case there is a problem to resolve the request, like a malformed URL.
If the request can be resolved error will be NULL and according with HTTP protocol and depending to the server that you are trying to connect, the NSHTTPURLResponse object will contain all the information about the request.
In general is an error think that every status code different than 200 is an error, for example for a REST based API 204 mean empty data, and in this case the request is finished successfully but the requested resource is just empty data, and this is not an error.
So about your question, is absolutely fine that error is NULL most of the time, if is not mean that there is an issue before reach the target server, in general you have to consider both, error and according to the server that you are trying to talk the status code maps, in most of cases the REST pattern
I have a wrapper for encrypting and decrypting using CommonCryptor. Occasionally the decryption process will fail, in which case I fill an error like so:
if (result == kCCSuccess) {
cipherData.length = outLength;
} else {
if (error) {
*error = [NSError errorWithDomain:kBridgeEncryptorErrorDomain
code:result
userInfo:nil];
}
return nil;
}
And then I log the error like this:
if (error != nil) {
DDLogError(#"Decrypt fail %i, %#", [error code], [error localizedDescription]);
}
However, this ends up generating strings like:
2013-01-09 09:15:19.753 [BridgeEncrypter decryptDataFromData:] [Line 83] E: Decrypt fail -4304, The operation couldn’t be completed. (com.***.bridgecrypt error -4304.)
Where the -4304 could be any of the error codes in CommonCryptor.h (-4300 to -4305). Is there a good way to map the error codes to their string values, or do I need to have a switch statement that adjusts the string by hand? If I do have to depend on a switch, would best practice be to put it where the issue is logged or where the error is generated?
I'm not sure what you're looking for here. I'm not familiar with CommonCryptor or how error messages are handled in it.
I can recommend that you lean on NSError and it's userInfo and NSLocalized*Key feature.
For example, if you set a NSLocalizedDescriptionKey in the userInfo dictionary, error:
NSDictionary userInfo = #{
NSLocalizedDescriptionKey : #"This is the error message I want users to see"
};
*error = [NSError errorWithDomain:kBridgeEncryptorErrorDomain
code:result
userInfo:userInfo];
Then This is the error message I want users to see is the string returned by -localizedDescription. Then the calling code can use the string to display a message to the user without needing to reinterpret it.
As to the question of how to link error codes to messages you want users to see, there could be a CommonCryptor function that converts error codes to human readable string. If not, then you could write your own. I would recommend using a switch.
NSString *MyCodeToLocalizedDescription(CCCryptorStatus cryptorStatus)
{
switch(cryptorStatus) {
case kCCDecodeError: return #"This is the error message I want users to see";
…
default: return #"Oh noes, unknown error";
}
}
At that point setting the error is:
NSDictionary userInfo = #{
NSLocalizedDescriptionKey : MyCodeToLocalizedDescription(result)
};
*error = [NSError errorWithDomain:kBridgeEncryptorErrorDomain
code:result
userInfo:userInfo];