Calling an API with HTTP Header and Body with Alamofire - swift

I'm trying to recreate a POST request that already works in Postman in Swift 3 with Alamofire 4, but i'm always getting a Status Code 400 "Bad Request". I am out of ideas about what I'm doing wrong here.
This is the request in Postman, additionally there is a username and password in the Body in JSON format:
Reading the Docs for Alamofire, I thought this should be the correct Swift code:
func login(as username: String, withPassword password: String) {
let url = "https://api2.drive-now.com/login"
let parameters: Parameters = [
"username" : username,
"password" : password
]
let loginHeaders: HTTPHeaders = [
"Accept" : "application/json;v=1.6",
"Accept-Encoding" : "gzip, deflate, sdch",
"Accept-Language" : "de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4",
"Connection" : "keep-alive",
"Host" : "api2.drive-now.com",
"Origin" : "https://de.drive-now.com",
"X-Api-Key" : "adf51226795afbc4e7575ccc124face7",
"X-Language" : "de",
"Content-Type" : "application/json"
]
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: loginHeaders).responseJSON { response in
print("Request: \(response.request)") // original URL request
print("Response: \(response.response)") // HTTP URL response
print("Data: \(response.data)") // server data
print("Result: \(response.result)") // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
}
My console output is:
Request: Optional(https://api2.drive-now.com/login)
Response: Optional(<NSHTTPURLResponse: 0x6000000266a0> { URL: https://api2.drive-now.com/login } { status code: 400, headers {
Connection = close;
"Content-Length" = 181;
"Content-Type" = "text/html";
Date = "Tue, 13 Dec 2016 22:12:46 GMT";
Server = "nginx/1.4.6 (Ubuntu)";
} })
Data: Optional(181 bytes)
Result: FAILURE
Is there a custom session manager maybe that I have to implement? Or do you know of any debugging methods I could use here?

A friend accustomed to the API did help me resolve the issue: it was a default header field that seems to be added by Alamofire to every call. The API didn't accept calls with a "User-Agent" set (don't ask me why).
To help others who might have the same problem, I share the steps I went through to find and resolve the issue:
I made Alamofire.request(...) into a variable named postage (you can call it however you like, of course)
I added debugPrint(postage) to the end of the login-function
The output showed the additional header field
I constructed a custom SessionManager like below
var headers = Alamofire.SessionManager.defaultHTTPHeaders
headers.removeValue(forKey: "User-Agent")
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = headers
api = Alamofire.SessionManager(configuration: configuration)

Related

Set hostname on HTTPRequest for testing

In writing tests for my Vapor 3 app, I've run into an issue where a certain framework is reliant on checking the incoming requests hostname
guard let peerHostName = request.http.remotePeer.hostname else {
throw Abort(
.forbidden,
reason: "Unable to verify peer"
)
}
It would seem that when testing a request like below
let emails = (0...10).map { "email#test.co"}
let responder = try app.make(Responder.self)
let request = HTTPRequest(method: .POST, url: URL(string: "\(usersURI)/create")!, headers: headers)
let wrappedRequest = Request(http: request, using: app)
try wrappedRequest.content.encode(createUserReq)
try responder.respond(to: wrappedRequest)
Then the requests hostname is empty and thus an error is thrown. Is there any way I can manually set the hostname of the request? The hostname property is get only, so I can't set it that way
The solution was to add a 'forwarded' header
var headers: HTTPHeaders = [
"Content-Type": "application/json",
"forwarded": "by=BY;for=127.0.0.1"
]

Swift - Request to REST Api - Not readable

I'm sending a request to an API, but it always responds with an error Not readable http body.
In message it returns:
exception = "class org.springframework.http.converter.HttpMessageNotReadableException";
httpCode = 400;
httpMessage = "Bad Request";
Could not read document: Unrecognized token 'product': was expecting ('true', 'false' or 'null')\n at [Source: java.io.PushbackInputStream#430128e1; line: 1, column: 9]; nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'product': was expecting ('true', 'false' or 'null')\n at [Source: java.io.PushbackInputStream#430128e1; line: 1, column: 9]
But product isn't boolean, but string.
Anyone know, what's wrong?
let api_key = Data(klic.utf8).base64EncodedString()
let parametry = ["product" : "STANDART"] as [String : Any]
let headers = ["authorization" : "Basic \(api_key)", "content-type" : "application/json", "cache-control" : "no-cache"]
Alamofire.request("https://stage.japostparcel.cz/api/v1/order/", method: .post, parameters: parametry, headers: headers).responseJSON { (response) in
//print(NSString(data: (response.request?.url)!, encoding: String.Encoding.utf8.rawValue))
print(response)
}
Your base url ends with order/ so you post something like order/?product= STANDART. I am pretty sure you need some kind of page ref in between and not /? something like
`order/order_page?product= STANDART`
So check the documentation

Alamofire Form Encoded POST Request Failing with responseSerializationFailed

Making a request with Alamofire (swift 4) to an api endpoint (form encoded content type) and passing a username and password via login. When testing in POSTMAN, this endpoint works without errors and returns valid JSON (see below).
My swift code is as follows:
let headers = [
"content-type": "application/x-www-form-urlencoded",
"cache-control": "no-cache"
]
let parameters = [
"username": "user#user.com",
"password": "password"
]
Alamofire.request("https://xxxxx.com/api/login/", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
print(response)
}
The response I get is as follows:
FAILURE: responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}))
Any insight would be appreciated!
Change JSONEncoding.default to URLEncoding.default as below,
Alamofire.request("https://xxxxx.com/api/login/", method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON { response in
print(response)
}

PayPal SDK request for access token returns code 401 (unauthorized)

I am trying to receive an access token from PayPal's server using the authorization code in a Sandbox environment. I believe the problem is in converting curl to an Alamofire request, but I'm not sure. Any help is appreciated.
Here is my code for sending authorization to server:
func sendAuthorizationToServer(authorization: [AnyHashable: Any]) {
let jsonAuth = JSON(authorization)
let headers = [
"content-type": "application/x-www-form-urlencoded; charset=utf-8",
]
let parameters = [
"client_id": Constants.payPalSandboxID,
"client_secret": Constants.payPalSandboxSecret,
"grant_type": "authorization_code",
"redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
"code": jsonAuth["response"]["code"].stringValue
]
Alamofire.request("https://api.sandbox.paypal.com/v1/oauth2/token", method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON { response in
print(response.request)
print(response.response)
print(response.data)
print(response.error)
}
}
I've checked jsonAuth["response"]["code"].stringValue and it is returning a correct authorization code.
My doubts are if it should be a .post request or a .get request, and if my credentials in the parameters are in the correct order/structure.
Here is the output of the last print statements:
Here is the SDK link for exchanging an authorization code for the access token:
https://github.com/paypal/PayPal-iOS-SDK/blob/master/docs/future_payments_server.md#exchange-authorization-code-for-refresh--access-tokens
import SwiftyJSON
import Alamofire
// ... ...
// ... ...
// ... ...
let credentialData = "\(paypalClientId):\(paypalSecret)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])
let headers = [
"Authorization": "Basic \(base64Credentials)",
]
let params:[String: Any] = [
"grant_type": "client_credentials",
]
Alamofire.request(tokenAPI, method: .post, parameters: params, encoding: URLEncoding.default, headers: headers)
.validate()
.responseJSON { (response) in
debugPrint(response)
switch response.result {
case .success(let data):
let json = JSON(data)
let accessToken = json["access_token"].stringValue
break;
case .failure(let error):
debugPrint(error)
debugPrint(response.data)
}
}
Please note that encoding must be URLEncoding.default not the JSONEncoding.default

401 error using Alamofire and swift when trying to connect to my server

Ok so I am trying to download a json file as a string and parse it out latter. But I have to download it from my webpage first. This webpage needs a username and password to get to it. This has been giving me a 401 error so its not sending the username or password. How can I add the username and password to the request?
print("Downloading the json file")
let plainString = "\(myUserName):\(myPassword)" as NSString
let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders = ["Authorization": "Basic " + base64String!]
Alamofire.request(.GET, promoUrl)
.response {(request, response, _, error) in
print(response)
}
This is the result from it
Optional(<NSHTTPURLResponse: 0x7fe103818790> { URL: http://xxxapi/1.0/promotions } { status code: 401, headers {
"Cache-Control" = "no-cache, no-store, max-age=0, must-revalidate";
Connection = "keep-alive";
"Content-Length" = 186;
"Content-Type" = "application/json;charset=UTF-8";
Date = "Thu, 12 May 2016 01:36:33 GMT";
Expires = 0;
Pragma = "no-cache";
Server = "Apache-Coyote/1.1";
"Www-Authenticate" = "Basic realm=\"Realm\"";
"X-Content-Type-Options" = nosniff;
"X-Frame-Options" = DENY;
"X-XSS-Protection" = "1; mode=block";
Thank you very much for any help with this
Hi every one so after working on it a bit more i got it to work. I got rid of all the stuff on top and went simple. It worked better and is easier to do
here is the working code for anyone who wants it
Alamofire.request(.GET, promoUrl, parameters: [myUserName:myPassword])
.authenticate(user: myUserName, password: myPassword)
.response {(request, response, data, error) in
print(response)
}
The key difference is this
parameters: [myUserName:myPassword]
This loads my password and username into the url. It may not be the best way of doing this but it works for my needs for now