Swift Alamofire POST request becomes GET - swift

That's the code I'm using to make POST request to my Flask server in localhost:
func data_request() {
let url:NSURL = NSURL(string: "http://192.168.1.192:9880/api/register")!
Alamofire.request(.POST, url, parameters: ["login":"login", "password" : "12345"]).responseJSON { response in
switch response.result {
case .Success:
NSLog("Validation Successful")
case .Failure(let error):
NSLog("\(error), \(String(data: response.data!, encoding: NSUTF8StringEncoding))")
return
}
if (response.result.value as? [String: AnyObject]) != nil{
print(response.result.value)
}
}
}
But it sends GET request! Both server and local proxy tell it was GET request - that's what Burp has intercepted:
GET /api/register/ HTTP/1.1
Host: 192.168.1.192:9880
Accept: */*
User-Agent: Project Manager/Roman-Nikitin.Project-Manager (1; OS X 10.11.3)
Accept-Language: en;q=1.0, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5
Accept-Encoding: gzip;q=1.0, compress;q=0.5
Connection: close

I had the same problem, you just have to put / at the end of URL. Problem is in Alamofire, I think. It works weird with normal server redirections from www.domain.com/something to www.domain.com/something/

For anyone getting here because they have the same problem (like me): In my case, I already had a / at the end and I had to remove it.
Based on this answer, it seems to be the server redirecting the original POST request to a GET request. So either figure out what your server requires (trailing slash or no), or set up the server like in the linked answer.

Related

make a POST request with json data

I have a problem on my code that I can't figure it out, I am making a POST request to a endpoint that is used for log-in and I am not receiving anything back, But when I send the same request to Postman I receive the valid data so the problem is at my code and truly I have no clue how to get this one fixed now friends.
The code:
func SignIn() {
let json: [String: Any] = ["User": "test", "Password": "test123"]
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .fragmentsAllowed)
let url = URL(string: "https://testingkrupi/Signin")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
}
The request that must be send:
POST http://testingkrupi/Signin HTTP/1.1
User-Agent: Fiddler
Host: localhost
Content-Length: 59
Content-Type: application/json; charset=utf-8
{
"User": "test",
"Password": "test123"
}
The response I must get:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 14 Apr 2022 07:00:29 GMT
Content-Length: 351
{
"StatusCode": 0,
"Result": {
"First": "test",
"Last": "test123",
"CompleteName": "test test123",
"PhoneNumber": "+512 512321 125",
"Email": "randomtest#gmail.com",
"IsConnectedToCustomer": true,
"Token": "423tj32o3jg230g923gj023gijf2o02",
"TokenExpireDate": "2020-05-14T09:00:29.2"
}
}
This answer is more about debugging skill you need to have in order to advance.
First, you need to find where lies the issue. You know it's in SignIn() (I guess).
Now, let's start by adding some informations to know what's happening, if you don't know (yet) how to use breakpoints, at least add logs to know what's happening:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
print("HTTPResponse: \(response)")
guard let data = data, error == nil else {
print(error ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
} else {
//Here you test nullability of responseJSON AND if it's a [String: Any], so what if it's not null, but not a [String: Any]?
print("Response JSON is nil, or is not a [String: Any]")
}
}
That should at least give you some feedbacks, which one is printed.
Now, let's continue:
Each time you do a try? (with the question mark), you are saying this: I know that the method can throw an error, and error which often has a good explanation on why it failed exactly, helping me into debugging it, but I decided to not care about it, I'll just ignore it.
That's the same as ignoring the Gas/Fuel tank empty warning on your car, don't complain later that it was beeping, but you just ignore it when the car will stop because there is no more gas/fuel.
You can use then a try! (with exclamation mark), which will show the error in console, but will make crash the app. At least you shouldn't miss it.
But, it's recommended for try to implement a proper do/try/catch:
do {
let responseJSON = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
} catch {
print("Error while calling JSONSerialization: \(error)")
print("Got stringified response: \(String(data: data, encoding: .utf8) ?? "not utf8 stringifiable data")")
//If you enter in the "not utf8 stringifiable data" output, then, check the data, maybe use hex representation, since not all data, like jpg data can be UTF8 interpreted as such, but that's another case.
}
For now, I don't know the output in console, but:
print("HTTPResponse: \(response)") might give you important info, usually if it's not a 200 HTTP code, and you'll have at least a "sure" print in console to ensure that the closure is correctly called.
print("Error while calling JSONSerialization: \(error)") & print("Got stringified response: \(String(data: data, encoding: .utf8) ?? "not utf8 stringifiable data")") should give you info on the real data you are receiving, and maybe why you are receiving that one.
Now, why I printed the "Stringified response" & HTTPResponse? Simply because developers often mistake what they should received with what they will receive, and ignore totally what they are in fact receiving. Don't be like that, read what you are receiving, and fix your issues. Simply reading the doc and even if you implement it correctly doesn't mean that you'll necessary succeed in first attempt, from an error on your implementation call, a server issue, an outdated doc, etc, everything can occurs.
Let's keep digging, you are using POSTMAN and it's working, did you know that POSTMAN can generate Swift URLSession Code '(and cURL too, which is often also useful)? It's not "beautiful Swift" code, but it's working one. Test that one, and if it's working, compare it with you own implementation. It can show a forgotten parameter, any silly mistake or misunderstanding of the API documentation.
Unrelated but:
You should start naming your methods with a lower case:
func SignIn()
->
func signIn()
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .fragmentsAllowed)
There is no need for .fragmentsAllowed here. You aren't using a String at top level, it's a Dictionary.

How to call a post API which has parameters like get api in Alamofire Swift?

I have checked many stack overflow questions but unable to find answer for the following problem. How can I request a post api which has parameters like get api using Alamofire Swift?
Request:
http://dev.practice.com/api/v1/test/apply?id=51&social_id=2
Body:
"url": https://www.social.com/some_image_url
I have tried with the following way but it's not working:
let urlString = "http://dev.practice.com/api/v1/test/apply?id=51&social_id=2"
Alamofire.request(urlString, method: .post, parameters: ["url": "https://www.social.com/some_image_url"],encoding: JSONEncoding.httpBody, headers: header).responseJSON {
response in
switch response.result {
case .success:
print(response)
break
case .failure(let error):
print(error)
}
}
The answer is:
We have to request the get api like parameters as queryString and post body parameters as httpBody with the request.

Alamofire POST encoding issue

I'm trying to login to a web service as follows:
func Login(completionHandler:#escaping (Bool) -> ()) {
let url = MyUrl
let parameters: Parameters = [
"Password":password,
"StayLoggedIn":NSNumber(value: true),
"UserName":username
]
var headers:HTTPHeaders = commonHeaders()
headers["Content-Type"] = "application/json;charset=UTF-8"
Alamofire.request(url, method:.post, parameters:parameters, headers:headers).responseJSON { response in
switch response.result {
case .success:
debugPrint(response)
case .failure(let error):
print(error)
}
}
}
Somehow, the server returns a 501 error.
I'm in the process of rewriting an Obj-c app in Swift, and replacing NSURLSessions with Alamofire into the bargain.
What I see in my working obj-c app, is that the headers sent to the server contain
Content-Type: application/json;charset=UTF-8
As you can see, I explicitly add this header to my request, but somehow, it is not sent to the server.
What I also see, is that my login credentials are not sent to the server.
So, I guess my question is: how do I tell Alamofire to use the correct encoding/content type?
try this encoding application/x-www-form-urlencoded instead of application/json;charset=UTF-8

Alamofire responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength)

I had some working code that was getting results from a MySQL DB on a remote web server. It is no longer working and I keep getting the message responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength). Here is some code...
Alamofire.request(ADS_URL, method: .get).validate().responseJSON { response in
print("Request: \(String(describing: response.request))") // original url request
print("Response: \(String(describing: response.response))") // http url response
print("Result: \(response.result)") // response serialization result
switch response.result {
case .success(let value):
let json = JSON(value)
print ("JSON: \(json)")
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)") // original server data as UTF8 string
}
case .failure(let error):
print("Error while querying database: \(String(describing: error))")
return
}
}
I am also using SwiftyJSON. Here are the results of the code...
Request: Optional(http://doyouado.com/adscan/get_ads)
Response: Optional(<NSHTTPURLResponse: 0x17502f3a0> { URL: http://doyouado.com/adscan/get_ads } { status code: 200, headers {
Connection = "keep-alive";
"Content-Length" = 0;
"Content-Type" = "text/html; charset=UTF-8";
Date = "Mon, 18 Sep 2017 16:04:37 GMT";
Server = "nginx/1.12.1";
"Set-Cookie" = "ado_session=a%3A5%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%225019d90891c70c81df8ebc2fe754a68f%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A15%3A%22109.150.214.128%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A86%3A%22ADoBroadcaster%2F1.0+%28com.GaryFrank.ADoBroadcaster%3B+build%3A1%3B+iOS+10.3.3%29+Alamofire%2F4.5.0%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1505750677%3Bs%3A9%3A%22user_data%22%3Bs%3A0%3A%22%22%3B%7D3130ef6f5541e6f944da5a5a1292350bf203fa1b; expires=Mon, 18-Sep-2017 18:04:37 GMT; Max-Age=7200; path=/";
} })
Result: FAILURE
Error: responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength)
I have tried using .response and .responseString, but I get no information returned. I am completley stumped. This was all working fine. Hopefully there is someone that can shed some light on this?
Just simply change .responseJSON to .responseData.
And after this parse data:
let jsonDecoder = JSONDecoder()
let parsedData = try jsonDecoder.decode(T.self, from: data)
and no error:
(Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength)
What worked for me was changing the encoding from JSONEncoding.default to URLEncoding.default!
Updating from Alamofire 4 to 5 caused the issue in my case.
By default, it seems that Alamofire 5 returns the error Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength for empty response body with status code 200. So adding 200 to the list of emptyResponseCodes resolved the issue for me:
request.responseData(emptyResponseCodes: [200, 204, 205]) { ... } // the default is [204, 205]
What worked for me was changing from .responseData to .response
Commonly this error comes when your API is 'GET' type and you pass 'POST' type.
The same problem I faced and my solution is I replace .post to .get and then this error removed.
For AFNetworking 3.0 :-
go given path,
pods > Pods > AFNetworking > Serialization > AFURLResponseSerialization.m
then replace line no 228 (self.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript", nil];)
with
self.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript", #"text/html", nil];
Because of your response in form of text/html but that is not mentioned in AFNetworking then we add it manually.
Note:- I debugging this problem for Alamofire.
When server sends back no response, Alamofire shows this message in the .failure block if you are printing the error message. Technically it is not an error. Alamofire didn't show this message in its earlier versions, but since one of the recent updates it started showing it.
As I said it is not really an error, but to me its a bug in Alamorfire. And it is very annoying and misleading to keep seeing this in your log when there is no error on your client or server side.
Here is how I silent it:
if (response.data?.count)! > 0 {print(error)}
And I do it when there is no response from the server, which is the expected behaviour since server is not supposed to send response in some cases.
Alamofire.request(MY_URL, method: .get, parameters: ["blabla": blablabla])
.validate(statusCode: 200..<300)
.responseJSON {
response in
switch response.result {
case .success(let value):
self.processResponse(value)
case .failure(let error):
if (response.data?.count)! > 0 {print(error)}
}
}
So the error message doesn't shows when nothing is returned from the server. In my opinion this should be the default behaviour.
Though the question is quite old, I wanted to provide to others what I recently discovered.
Since the error message is very generic and it doesn't help much, check that the url format you are using is correct. I've gotten this only to discover that the url format was incorrect. Once fixed things started working fine.

What is the difference between HTTP parameters and HTTP headers?

I read this question but it didn't answer my question.
To me Headers and Parameters are both dictionaries with the difference that headers is [String : String] while Parameters is [String : AnyObject]? and so if your parameters are also Strings then you could send them within the headers (while using a 'x-' prefix to signify they aren't standard headers) which is a common but not good practice.
Is that correct?
Are there other difference between headers and parameters?
What kind of other non-String types would you be sending using parameters?
Alamofire Request method
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request
{
return Manager.sharedInstance.request(
method,
URLString,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
As an example I have seen people passing ["x-ios-version" : UIDevice.currentDevice().systemVersion] or build versions through headers
The accepted answer is very practical. Make sure you see it. But there are two foundational differences I will discuss in depth:
Where header and parameters are placed in an HTTP Request
A URL is different from an HTTP Message. An HTTP Message can either be a Request or a Response. In this answer I will focus on the request.
An HTTP Request is made up of mainly the url, http-method, http-headers (there are other chunks in it, but I'm just mentioning the ones we care about the most)
Request = Request-Line ; Section 5.1
*(( general-header ; Section 4.5
| request-header ; Section 5.3
| entity-header ) CRLF) ; Section 7.1
CRLF
[ message-body ] ; Section 4.3
A request line is:
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
CLRF is something like a new line.
For more see here and here. You might have to do some back and forth between the links til you get it right. If you really wanted to go deep then see see this RFC
So basically a request is something like:
POST /cgi-bin/process.cgi?tag=networking&order=newest HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Content-Type: text/xml; charset=utf-8
Content-Length: 60
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
first=Zara&last=Ali
The query params are within the URL. HTTP Headers are NOT part of the URL. They're part of the HTTP Message. In the above example, query params is tag=networking&order=newest, the headers are:
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Content-Type: text/xml; charset=utf-8
Content-Length: 60
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
So when you make a network request, you're sending a structured STRING using the http protocol. That string is sent through a TCP connection.
2 - Why and where one is preferred over the other
From discussion with Rob in chat:
The criteria is that if it's information about the request or about the client, then the header is appropriate.
But if it's the content of the request itself (e.g. what you are requesting from the server, some details that identify the item to be returned, some details to be saved on the web server, etc.), then it's a parameter e.g.:
Parameter
Let's say you're requesting an image for a product. The product id may be one parameter. The image size (thumbnail vs full size) might be another parameter.
The product id and requested image size are examples of "some detail" (or parameter) being supplied as part of the content of a request.
Header
But things like the request is JSON or x-www-form-urlencoded are not the content of the request, but meta data about the request (especially since it's necessary for web service to know how to parse the body of the request). That's why it's a header.
Most likely if your app makes various requests, its headers would have a lot in common. However the parameters due to the fact that they are content based should be more varied.
Construction using URLComponents
class UnsplashRequester {
static let session = URLSession.shared
static let host = "api.unsplash.com"
static let photosPath = "/photos"
static let accessKey = "My_access_key"
static func imageRequester(pageValue: String, completion: #escaping (Data?) -> Void) {
var components = URLComponents()
components.scheme = "https"
components.host = host
components.path = photosPath
// A: Adding a Query Parameter to a URL
components.queryItems = [URLQueryItem(name: "page", value: pageValue)]
let headers: [String: String] = ["Authorization": "Client-ID \(accessKey)"]
var request = URLRequest(url: components.url!)
for header in headers {
// B: Adding a Header to a URL
request.addValue(header.value, forHTTPHeaderField: header.key)
}
let task = session.dataTask(with: request) { data, _, error in
}
}
}
Here is the list of differences:
They are designed for different purposes. Headers carry meta info, parameters carry actual data.
HTTP Servers will automatically un-escape/decode parameter names/values. This does not apply to header names/values.
Header names/values need to be manually escaped/encoded at client side and be manually un-escaped/decoded at server side. Base64 encoding or percent escape is often used.
Parameters can be seen by end-users (in URL), but headers are hidden to end-users.