Status code 400 while making POST request in swift - swift

I keep getting this error which I don't know how to resolve!
I'm trying to parse access token from a web API and for that I've set up post request.
Somehow it's throwing an error on 'responseString' line.
import UIKit
import Foundation
class ViewController: UIViewController, NSURLConnectionDelegate {
#IBOutlet var userName: UITextField!
#IBOutlet var password: UITextField!
#IBAction func submit(sender: AnyObject) {
let postString = "username=" + userName.text + "&password=" + password.text + "&grant_type=password";
let data: NSData = postString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = data.base64EncodedStringWithOptions(nil)
let postLength=NSString(format: "%ld", data.length)
let url = NSURL(string: "http://www.myurl.com/Token")
let request = NSMutableURLRequest(URL: url!);
request.HTTPMethod = "POST"
request.HTTPBody = data
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue(base64LoginString, forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {data, response, error in
if error != nil {
println("error = \(error)")
return
}
//print out response object
println("******* response = \(response)")
//print out response body // THIS LINE THROWS STATUS CODE 400 ERROR
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("******* response data = \(responseString)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
if let parseJSON = json {
var token = parseJSON["access_token"] as? String
println("token: \(token)")
}
}
task.resume()
}
This is the error
******* response = <NSHTTPURLResponse: 0x7ff1eb4496d0> { URL: https://www.myurl.com/Token } { status code: 400, headers {
"Cache-Control" = "no-cache";
"Content-Length" = 34;
"Content-Type" = "application/json;charset=UTF-8";
Date = "Mon, 10 Aug 2015 20:16:14 GMT";
Expires = "-1";
Pragma = "no-cache";
"X-Powered-By" = "ASP.NET";
} }

Your error seems to be incorrect usage of basic auth format. It should be like this:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
where
QWxhZGRpbjpvcGVuIHNlc2FtZQ==
is username:password in base64
So in your case it is like this :
let postString = userName.text + ":" + password.text ;
let data: NSData = postString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = data.base64EncodedStringWithOptions(nil)
let postLength=NSString(format: "%ld", data.length)
let url = NSURL(string: "http://www.myurl.com/Token")
let request = NSMutableURLRequest(URL: url!);
request.HTTPMethod = "POST"
request.HTTPBody = ""
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue("Basic " + base64LoginString, forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("0", forHTTPHeaderField: "Content-Length")
btw it would be a lot easier to find your error if you provide your api url

The error code 400 says that the server doesn't understand for request, maybe the request is malformed. This is very likely a client side issue. However, I've written a simple python http server, and used the code above (with only change to the URL entry point), everything works. I strongly suggest that you should check the API documentation on the backend and find out why your backend server doesn't recognize your request. Your client side code seems correct.
Try this on your own:
Create myserver.py, and add the following lines to it:
from flask import Flask, request
app = Flask(__name__)
#app.route('/Token', methods=['POST'])
def hello_world():
print(request.form)
return 'Hello World!'
if __name__ == '__main__':
app.run()
Run this python script from terminal, and change http://www.myurl.com to http://127.0.0.1:5000, you'll see that the request gets through, and the server can correctly parse your username password, etc.
It's always a good idea to try to setup your own local web server to mock the backend during debugging/development according to backend API specification.

try this one
//first make json object.you can use your object for this one.
let JSONObject: [String : AnyObject] = [
"DeviceHash" : UUIDValue,
"DeviceName" : deviceNameValue!,
"SerialKey": serialKeyValue!,
]
if NSJSONSerialization.isValidJSONObject(JSONObject) {
var request: NSMutableURLRequest = NSMutableURLRequest()
let url = "Your Url"
var err: NSError?
request.URL = NSURL(string: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: NSJSONWritingOptions())
print(JSONObject)
} catch {
print("bad things happened")
}
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) {(response, data, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
if error != nil {
print("error")
}else {
print(response)
}
}
}

Related

How to use POST request to Firebase Firestore from swift?

I would like to use the Firestore REST API from Swift, because I am using Siri Shortcut Intents, where I am not able to use the native SDK.
What I have tried so far is to create an URLSession with "POST" httpmethod, but no luck. I have been able successfully to create document to use the form found on firestore website. But I could make successful Swift version of it.
Here is the code I have tried:
private func addTask() {
let parent = "projects/reality-kanban/databases/(default)/documents/l3VXrtTLoz11VGn60ott"
let collectionId = "A33XrtfL2ea3dG340era"
let urlString = "https://firestore.googleapis.com/v1/\(parent)/\(collectionId)"
let requestBody = DocumentBody(name: parent, fields: RequestTask(description: "test")) // it is a codable struct
let jsonData = try! JSONEncoder().encode(requestBody)
print(String(data: jsonData, encoding: .utf8)!)
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
print("Invalid Response received from the server") // this is what I get
return
}
}
task.resume()
}
This is the error I get: Invalid Response received from the server (400)
Add --debug to the command you are running to corroborate if you have set the right project.

Using Swift with URLSession works with GET and PUT, but it gives error 405 Method Not Allowed when I use POST

I develop an iOS app using Swift.
I use the following method below to access an login ENDPOINT with PUT method.
let loginData = LoginModel("myUser","myPassword")
var loginClassJson:Data?
do{
loginClassJson = try JSONEncoder().encode(loginData)
} catch {
fatalError("Unable To Convert in Json")
}
let completeUrl = URL(string: RESconstantes.URL_PRINCIPAL_TREINAGEDAVE + "/login" )!
var request = URLRequest(url: completeUrl)
let myConfig = URLSessionConfiguration.default
let base64LoginString = EndpointController.getBase64StringLoginWithUserAndPasswordV2()
myConfig.httpAdditionalHeaders = ["Authorization" : base64LoginString]
request.httpMethod = "PUT"
request.setValue("\(String(describing: loginClassJson!.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = loginClassJson
let sessionDelegate = SessionDelegate()
let urlSession = URLSession(configuration: myConfig, delegate: sessionDelegate, delegateQueue: OperationQueue.main)
let task = urlSession.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
if let error = error{
print("errorX: ")
print(error)
return
}
if let data = data{
let returnData = String(data: data, encoding: String.Encoding.ascii)
print("dataX: ")
print(returnData)
}
if let response = response{
print("responseX: ")
print(response)
}
})
task.resume()
print("END")
This is my URLSessionDelegate class
class SessionDelegate:NSObject, URLSessionDelegate
{
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// usado para fazer o bypass na autenticação self-signed do certificado do servidor
// We've got a URLAuthenticationChallenge - we simply trust the HTTPS server and we proceed
print("start didReceive challenge 1")
if true {
print("didReceive challenge 2")
completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}else{
completionHandler(.performDefaultHandling, nil)
}
}
It works perfectly for me, but now I try to create a code to access another ENDPOINT with POST method
let resDadoModel = ResDadoModel.getResenhaById(1)
let jsonRequestUploadResenha = ResDadoModel.createMockJsonObjectResenhaDados(resDadoModel)
let json: [String: Any] = jsonRequestUploadResenha
guard let jsonData:Data = try? JSONSerialization.data(withJSONObject: json) else {
print("guard jsonData error")
return
}
let completeUrl = URL(string: RESconstantes.URL_PRINCIPAL_TREINAGEDAVE + "/validaResenha" )!
var request = URLRequest(url: completeUrl)
let myConfig = URLSessionConfiguration.default
let base64LoginString = EndpointController.getBase64StringLoginWithUserAndPasswordV2()
myConfig.httpAdditionalHeaders = ["Authorization" : base64LoginString, "Content-Type":""]
request.httpMethod = "POST"
request.setValue("\(String(describing: jsonData.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
let sessionDelegate = SessionDelegate()
let urlSession = URLSession(configuration: myConfig, delegate: sessionDelegate, delegateQueue: OperationQueue.main)
let task = urlSession.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
if let error = error{
print("errorX: ")
print(error)
return
}
if let data = data{
let returnData = String(data: data, encoding: String.Encoding.ascii)
print("dataX: ")
print(returnData)
}
if let response = response{
print("responseX: ")
print(response)
}
})
task.resume()
print("END")
But the code that I use to access the ENDPOINT called "validaResenha" is not working properly, I get a 405 method not allowed error.
I get the following response data
<NSHTTPURLResponse: 0x600002028560> { URL: https://my_url_endpoint/api/spservicos/v1/validaResenha } { Status Code: 405, Headers {
Allow = (
"POST, OPTIONS"
);
"Cache-Control" = (
"no-cache=\"set-cookie, set-cookie2\""
);
Connection = (
"Keep-Alive"
);
"Content-Language" = (
"en-US"
);
"Content-Length" = (
0
);
"Content-Type" = (
"text/plain"
);
Date = (
"Thu, 23 Dec 2021 23:16:21 GMT"
);
Expires = (
"Thu, 01 Dec 1994 16:00:00 GMT"
);
"Keep-Alive" = (
"timeout=10, max=100"
);
"Set-Cookie" = (
"LtpaToken2=L6DEf/sqCSjiI1rePW3wEWZo40oNAsxmNVBNTpIRm3FZZRSSgaqmUTDYdjTq2PNE4+FhiIOKw7Xzuta4+LpD3cUB8QKZQ/KVom/rFFQ50XNkpQezmgMlgsmDDgtodRxVU5eyo1P1NP6r/3M55eY4HkeD583kXQB3/+EH3dIryo0ii6Jn6PrxaspX5noEo0eSt+yF2AylLdU66fCcSMJw7LCrB8Tulna4xHe4Nb9i+O5z2mnTXoIgbozDGuXfS6Y20zPrsaN62Bx1X/nySf1luf1QMhrt6P4SPF6GVudm0s/Db9dS0b444kJA4kMSJ0NbZ2khMzV1zSg3eZY6xZg2kidV8Qczpe5bL2/DNrPQY/CrUo8wcdFE1ebfxDcVrjv3G+nH6uKOPWtbcHHx9Wp1gvHLxj3cJ5MP43AzxW/7GXPA7QlsmlquxW1Ck7OypsP2hrYCvCWubjGdM51cg8uqhIonI+uXRO6BlcXIsPOfpR+LbQfDNo+9vzXzB+CZKZmYnBX63ffWhX09Cr+Ua0a2Sw8mOcE5jXImlO49+ak0FHPkiiaSABzuOl6ALYg9J6LCxjm6MC9bKd7KbMPueJI/ugVeMyphQwss5AHxic8fVmo+7/XNRT6zr4I/01N8xFQsqrvx5+i2AhxWO1bdDKmpZQLPoTHMD7TPcFBkwDXLVqXPXkpkcGvg3mI8ssKOOlxwJT7/SETcqrCY5O8Yr505qdeZiNIj4kjKiLoLuNpE+ZI=; Path=/"
);
} }
Anyone has an idea why I was receiving a 405 error method not allowed? The POST method works for me if I use POSTMAN. It works if I use PUT or GET endpoints with Swift code, but it fails if I try to use endpoints with POST method in Swift.
I see something strange, the "Content-Type" is defined as text/plain in the response, but I set it as "application/json". I don't understand why the config was not being set.
If I call it via POSTMAN, it works, but for some reason I don't know why it not works when I use Swift.
--- EDIT ---
After #matt suggestion, I use Postman to generate the Swift code.
I copy and paste the Swift code to my project, this is the code:
var semaphore = DispatchSemaphore (value: 0)
let parameters = "{ \n \"token\":\"MY_TOKEN\",\n \"resenha\": {\n \"codAP\":\"353750303020001\",\n \"codPropriedade\":\"0\",\n \"cpfVeterinario\":\"01787568814\",\n \"coordGeoLat\": \"37.421565\",\n \"coordGeoLong\": \"-122.084\",\n \"cpfCnpjProdutor\": \"89058500810\",\n \"dataNascimentoAnimal\": \"01/08/1981\",\n \"fotos\": null,\n \"graficas\": null,\n \"id\": \"1\",\n \"idComposicaoPelagem\": \"50\",\n \"idCorOlhoDir\": \"39902\",\n \"idCorOlhoEsq\": \"39902\",\n \"idEspecie\": \"5\",\n \"idPelagem\": \"6\",\n \"idRaca\": \"34\",\n \"idResenhaAnterior\":\"0\",\n \"idSexo\": \"2501\",\n \"machoCastrado\": \"N\",\n \"microChipAnimal\": \"123456989012377\",\n \"microchipMae\": \"\",\n \"nomeAnimal\": \"MACADANIAS111\",\n \"numeroAssocRaca\": \"\",\n \"numeroPassaporte\": \"\",\n \"outrasCaracteristicas\": null,\n \"quantAnimaisEq\": \"05\",\n \"quantAnimaisAs\": \"0\",\n \"quantAnimaisMu\": \"02\",\n \"retifica\": false\n }\n}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://MY_ENDPOINT/validaResenha")!,timeoutInterval: Double.infinity)
request.addValue("Basic THIS_IS_BASIC_AUTH_VALIDATION", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("JSESSIONID=0000_B1PZRSVcyzEkDUkMxvk9ig:18jgnbg8n; LtpaToken2=q9JsIHVBKPsCYKGRohXJAKnXED3HRXXGlaswzYpnPSLS0B+c/WbiW+QcUMwmw/8xcb7VL1bVvbUh0ZAvMR3TNcGudWUkg9f0z5K0n0P2pJ5Frte6trqVLhPoKuI5E7zwC3Yg+XCsPBNFy0aukkrWNiCWAqbyGI3nir6UuX5qLER4H+bEYfk4cFw58eHGSIN/FTVjH7WW7aEAfkYNXxWzSDnNVJDtihZVXw+oJSfe74Vz8Scv33cPPZH2W74KvKwj09FOo+EJsvFcC2aDUQclYqwuo91HIaIpqcYb17cSCX95xn9KHErlC48M1bU03txKaDVcmUrOCrveCs7pVPNCz066cil5bzjXeYlXDlmUw5MT45Zgg8EmaJ9gi+iC2zPCU+W088OGEriphXpto40ww3irTN9rtnhIppB5U+drRFW6u25UmDkAjx899TNzC/XsJqkRXn9GinQv6xiD+Axnv3AgudQZBGyeYcNNJDRfA+jzbr6HE1NuNSkxY6aP1OvCLGkNDSA16chO7f4IjjR9jkvDH2m3+ajxa7as1rVNK9R0HHcZJLExvOJn3sJ1LKDwMNPTDjKGPLuWOVwTPQL2pzIitLjkeyx8A2Qcqo5p8U/+aa11Z/x8WA0bQRscZLWMtEHE6WLnHFqBaylCAzARF0Y5cOI/TYCW2xd99ux2WaJnumVlskr2uNCWdwSMOp78gvmzdmonQUH0Ko/k4wa8HcJPyMV5NK52gArIyGmeKXo=", forHTTPHeaderField: "Cookie")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()
print("end")
But I got now a 401 error. For some reason the basic Auth is not being accepted by the code or by the server.
I checked the user and password and they are correct.
I find out the fix for my problem.
I was using "http://MY_ENDPOINT/validaResenha" in my constants file instead of using "https://MY_ENDPOINT/validaResenha".
After add the "s" to "http" letter (it becomes "https") everything starts to work for me.
The strange part is that GET and PUT methods works fine because of the redirect from HTTP to HTTPS, but in the case of POST calls, I got this error.
It's fixed now.
iOS block HTTP use HTTPS or add to your Info.plist file

Swift post request error 500

Im trying to post a http request with parameters, the response is XML Formatted.
see: https://www.codepunker.com/tools/http-requests/64243-brrjq9t
now, when I try to code that im always getting the same error 500. what I do wrong?
guard let url = URL(string: "http://www.nakdan.com/GetResult.aspx") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
var bodyData = "txt=מה קורה&usr=&pass=&ktivmale=false"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest){
data,response, error in
print(response!)
if error != nil{
print("error")
return
}
}
task.resume()

401 Forbidden when sending email with Swift and Mailgun

I have the following code but consistently get error 401 forbidden when attempting to run it:
func email() {
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/{edited_out}/messages")! as URL)
request.httpMethod = "POST"
let data = "from: Swift Email <(test#test.com)>&to: [my_email_address#gmail.com,(my_email_address#gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
request.httpBody = data.data(using: String.Encoding.ascii)
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("key-{edited_out}", forHTTPHeaderField: "api")
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The error is:
url = https://api.mailgun.net/v3/{edited_out}/messages
response = <NSHTTPURLResponse: 0x600000226a20> { URL: https://api.mailgun.net/v3/{edited_out}/messages } { status code: 401, headers {
Connection = "keep-alive";
"Content-Length" = 9;
"Content-Type" = "text/html; charset=utf-8";
Date = "Thu, 29 Dec 2016 21:22:46 GMT";
Server = nginx;
"Www-Authenticate" = "Basic realm=\"MG API\"";
} }
response code = 401
If I send such a request via curl with my credentials it works fine.
Any ideas?
You need to set the uesrname and the password.
Something like this:
request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
and base64Credentials is the :
let credentials= String(format: "%#:%#", username, password)
let base64Credentials= credentials.data(using: String.Encoding.utf8)!
Finally got it working with the following code, if it helps anyone else:
func email() {
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/{edited_out}/messages")! as URL)
request.httpMethod = "POST"
let credentials = "api:key-{omitted}"
request.setValue("Basic \(credentials.toBase64())", forHTTPHeaderField: "Authorization")
let data = "from: Swift Email <(test#test.com)>&to: [my_email_address#gmail.com,(my_email_address#gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
request.httpBody = data.data(using: String.Encoding.ascii)
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
extension String {
func fromBase64() -> String? {
guard let data = Data(base64Encoded: self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
}
So I guess the answer from William would have worked with:
let base64Credentials = credentials.data(using: String.Encoding.utf8)!.base64EncodedString()
Instead of:
let base64Credentials = credentials.data(using: String.Encoding.utf8)!

NSURLSession parameters not recognized

Im attemping to make a HTTPRequest using NSURLSession. When I set the full url the request returns the correct data but when using parameters (NSJSONSerialization.dataWithJSONObject -> HTTPBody I get this error
error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
is there something im doing wrong here?
let json = ["api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted)
let url = NSURL(string: "https://api.themoviedb.org/3/discover/movie")!
let request = NSMutableURLRequest(URL: url)
request.HTTPBody = jsonData
request.HTTPMethod = "GET"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
} catch {
print(error)
}
}
This is not a duplicate! I looked at the suggested answer (none of them worked) before asking this question
In your case that issue can be solved by changing the request.HTTPMethod = "GET" to request.HTTPMethod = "POST"
You should not send HTTP Body in the get request, to send the data with the body you should change HTTPMethod to post
Note: Please check if this api method supports POST requests, if it don't support post you can't use it with http body/post, as per doc i only find 'get' request for the discover/movie which can be like this:
let url = NSURL(string: "http://api.themoviedb.org/3/discover/movie?api_key=YOUR_API_KEY")!
let request = NSMutableURLRequest(URL: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
if let response = response, data = data {
print(response)
print(String(data: data, encoding: NSUTF8StringEncoding))
} else {
print(error)
}
}
task.resume()
Ref: You can check more information from this url: http://docs.themoviedb.apiary.io/#reference/discover/discovermovie/get