Alamofire automatically escaping string in JSON object - ios

I'm sending a POST request to an api using Alamofire and SwiftyJSON and getting a weird issue where it seems Alamofire maybe automatically escaping forward slashes. Only one section of the JSON i'm sending to the server is having it's forward slashes escaped, and its a base64 image string.
I set some breakpoints and checked my local data on device and know that the data is fine and unescaped up until the point Alamofire sends off the request, but when it gets to the server, it is coming in escaped. Not sure if there is some flag I need to set to prevent this or something?
The Alamofire request:
Alamofire.request(.POST,
submitUrl,
parameters: ["data":completedBlob.object], //completedBlob is a SwiftyJSON object
encoding: .JSON ,
headers: self.submitHeaders
).responseJSON{ response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
guard json["error"].isExists() else{ callback?(success: false, jsonObject: nil);return}
let result = json
Settings.setAuthToken(self.db, token: self.authToken)
callback?(success: true, jsonObject: result)
}
case .Failure(let error):
print("An error occured processing request: \(error)")
print(response.result.value)
}
}
Base 64 string before Alamofire sends it off:
data:image/png;base64, forward slashes in data here are unescaped
Didnt paste the entire base64 string because its huge, but all of the forward slashes in the base64 string are escaped. Here is what the server receives:
{"inspector":"data:image\/png;base64,forward slashes in data here are escaped.
it seems its even encoding the media types
data:image/png;base64
Looked through Alamofire documentation, cannot find any mention of this issue, so i'm starting to feel like it might not be alamofire, but im not sure what else it could be?
Details: Running Xcode 7.3 | Swift 2.2 | Alamofire 3.30 | SwiftyJSON 2.3.1 | Both installed using Carthage.
Thanks guys!

Related

Text decoding issue from google translate API in iOS

So here is the URL:
https://translate.googleapis.com/translate_a/single?client=gtx&sl=ar-SA&tl=en-US&dt=t&q=سلام
The response is all fine when is hit from Browser or Postman. But when i do it through code, there are weird strings when getting a response.
I tried Alamofire:
Alamofire.request(urlString, method: .get, parameters: nil,encoding: JSONEncoding.prettyPrinted, headers: ["Accept":"application/json;charset=UTF-8" ]).responseJSON { response in
print(response)
}
The response string is something like this:
"\\U0637\\U00b3\\U0638\\U201e\\U0637\\U00a7\\U0638\\U2026"
and also this weird Arabic string.
"ط ط ط \\\"\\\" ط ... ... ...\"
I also tried hitting URL like this:
String.init(data: Data.init(contentsOf: URL.init(string: "https://translate.googleapis.com/translate_a/single?client=gtx&sl=ar-SA&tl=en-US&dt=t&q=%E2%80%8F%D8%B3%D9%84%D8%A7%D9%85")!), encoding: .utf8)
But the result is the same, some weird Arabic strings, which is different when is hit from browser or postman.
"ط ط ط \\\"\\\" ط ... ... ...\"
I am encoding URL before hitting URL. Also tried different encodings on url and string.
This was the issue with the parameters of API. I added these 2 parameters and it worked all fine.
ie=UTF-8
oe=UTF-8

Xcode iOS password get AF 'posted' with "some" hash / type of encryption - what kind?

I'm testing an app not yet published. I have a client side register/log-in, using Alamofire to post and then retrieve and parse JSON. Unless Alamofire has "blackboxed" some type of hash, I am not aware of having coded any kind of hash anywhere, yet.
When I went to look into why the test passwords (passed via SSL, HTTPS, and without any effort to encrypt, yet) were showing up on the server side looking like the result of a hash, I compared that result to a deliberate server side Sha256 hash (done on the raw, plain text password matching the original that got passed from the app). I am seeing this:
"ccc" ----> Sha256 hash = 64daa44ad493ff28a96effab6e77f1732a3d97d83241581b37dbd70a7a4900fe
"ccc" ----> "simple iOS post" (via Alamofire) = 9df62e693988eb4e1e1444ece0578579
As you can see, the values are very different, and this means unless I know what happened on the way over, I cannot authenticate anyone on the server side, nor can I use any server side password reset functions, because I have no idea what kind of hash was used.
Can anyone help me know what happened to hash the password?
Here's the simple Alamofire-based code doing the post (Xcode 9, Swift 4):
//creating parameters for the post request
let parameters: Parameters=[
"username":textFieldUserName.text!,
"password":textFieldPassword.text!,
"name":textFieldName.text!,
"email":textFieldEmail.text!,
"phone":textFieldPhone.text!,
"user_type":String(user_type),
"user_privileges":String(user_privileges)
]
print("Post Contents ('parameters') = \(parameters)")
//Sending http post request
Alamofire.request(URL_USER_REGISTER, method: .post, parameters: parameters).responseJSON
{
response in
//printing response
print(response)
//getting the json value from the server
if let result = response.result.value {
//converting it as NSDictionary
let jsonData = result as! NSDictionary
}
}
Well, I feel sheepish. I found a hash being applied in the PHP on the server side.
md5($pass);
No need to bother with this one. Now wish I had not even posted it. But, maybe it will help someone.

How to send `apikey` in header in Alamofire 4.5,Swift 4?

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
}

Why is one version of my http POST body not working?

I am trying to send something to an API using POST. The post body is made up of x 2 properties.
If I create the post body as one long string:
let postBody = "ministryId=nameOfMinistryHere&personId=1005" and then encode the string as follows urlRequest.httpBody = postBody.data(using: String.Encoding.utf8) it works perfectly.
But I am trying to create the post as a dictionary and then pass it to the API, but can't get it to work.
let postBody = ["ministryId":"nameOfMinistry", "personId":"1005"]
do {
try urlRequest.httpBody = JSONSerialization.data(withJSONObject: postBody, options: .prettyPrinted)
} catch {
print("problems serializing data")
}
When I use the latter option I am getting a 400 error from the server.
What am I missing?
Thanks in advance.
URLComponents is the class for dealing with multiple parameters. Code snippet:
let postBody = ["ministryId":"nameOfMinistry", "personId":"1005"]
let urlComponents = URLComponents(string: myURL)
let urlRequest = URLRequest(url: urlComponents.url!)
// transform the dictionary into queryItems
urlComponents.queryItems = postBody.map { URLQueryItem(name: $0, value: $1) }
urlRequest.httpBody = urlComponents.percentEncodedQuery?.data(using: String.Encoding.utf8)
thecloud_of_unKnowing answer to your comment as it was long i am posting it here -:
HTTP headers can be mainly classified into two types: HTTP Request Header Whenever you type a URL into the address bar and try to access it, your browser sends an HTTP request to the server. The HTTP request header contains information in a text-record form, which includes particulars such as the type, capabilities and version of the browser that generates the request, the operating system used by the client, the page that was requested, the various types of outputs accepted by the browser, and so on. HTTP Response Header Upon receiving the request header, the Web server will send an HTTP response header back to the client. An HTTP response header includes information in a text-record form that a Web server transmits back to the client's browser. The response header contains particulars such as the type, date and size of the file sent back by the server, as well as information regarding the server.SO you are just sending extra information to your server to let it know what kind of request it will accept.
Content-type: application/json; charset=utf-8 designates the content to be in JSON format, encoded in the UTF-8 character encoding. Designating the encoding is somewhat redundant for JSON, since the default (only?) encoding for JSON is UTF-8. So in this case the receiving server apparently is happy knowing that it's dealing with JSON and assumes that the encoding is UTF-8 by default, that's why it works with or without the header.
Simply make a dictionary as follows:
let jsonBody = ["username": email, "password": password]
Then you can do something like this:
let request = NSMutableURLRequest(url: NSURL(string: "YOUR URL") as URL)
request.httpBody = try! JSONSerialization.data(withJSONObject: jsonBody, options: .prettyPrinted)
Hope that helps!

How to set body type to JSON in Alamofire?

I'm working online with different people from different projects who take care of backend API webservice. Usually I don't have problems with sending and receiving JSON, but this time, I can't seem to be able to send JSON properly to the server.
Usually I use Alamofire to receive and send JSON message, and the usual call go like this:
let param = populateParamWithDictionary();
let url = "https://www.example.com";
Alamofire.request(.POST, url, parameters: param, headers: nil)
.responseJSON { response in {
// take care of response here
}
But this time, I got project which the backend programmer requires me to use OAuth v2. So, let's say I've develop a function which already take care of getting the access_token string. The function now become like this:
let param = populateParamWithDictionary();
let url = "https://www.example.com";
let headers : Dictionary<String, String> = [
"Content-Type":"application/json",
"Authorization":"Bearer \(access_token)"
];
Alamofire.request(.POST, url, parameters: param, headers: headers)
.responseJSON { response in {
// take care of response here
}
But instead of the result, I get 400 bad request error. I also even try this:
let param = populateParamWithDictionary();
let url = "https://www.example.com";
let headers : Dictionary<String, String> = [
"Content-Type":"application/json",
"Authorization":"Bearer \(access_token)"
];
Alamofire.request(.POST, url, parameters: param, encoding: ParameterEncoding.JSON, headers: headers)
.responseJSON { response in {
// take care of response here
}
But the result is even worse. This is what I get when I print the response.
FAILURE: Error Domain=NSURLErrorDomain Code=-1017 "cannot parse
response" UserInfo={NSUnderlyingError=0x7fbb505788f0 {Error
Domain=kCFErrorDomainCFNetwork Code=-1017 "(null)"
UserInfo={_kCFStreamErrorCodeKey=-1, _kCFStreamErrorDomainKey=4}},
NSErrorFailingURLStringKey=http://lfapp.learnflux.net/v1/me,
NSErrorFailingURLKey=http://lfapp.learnflux.net/v1/me,
_kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-1, NSLocalizedDescription=cannot parse response}
But the request works if I use REST client, by setting the headers to have the authentication and Content-Type, and have the parameters to be written as plain Content, e.g. in plain API in the body content.
How can I fix this?
EDIT: The part with the access token is already clear. The access token works. I can call an API successfully if the API doesn't requires any parameters (maybe because on the server, the code doesn't bother to even check or validate the body at all because it doesn't need anything from there, hence no error raised). The problem is when I make a request which needs any parameters.
The error you have is probably because of encoding: ParameterEncoding.JSON in the request. Try to change it to encoding: .URLEncodedInURL. If this doesn't help you, do add your parameters to the question and if you´re make a request to get the token do the following:
if let access_token = json["access_token"]!{
// Make the request here when you know that you have your token
}

Resources