How to translate a python script to swift (http post request)? - swift

I have a working python script that gets cookies from a site. I want to convert it to Swift script, but I do something wrong and can not understand where.
#!/usr/bin/env python
import warnings
import json
import os
import requests
auth_login='someName'
auth_password='somePassword'
passport_params = {'from': 'passport', 'retpath': 'https://192.168.0.1/passport?mode=passport', 'passwd': auth_password, 'login': auth_login, 'display': 'page'}
passport_session = requests.session()
passport_error_passwd = u"wrong pass"
passport_error_login = u"no such users"
print('Connecting to passport...')
try:
passport_request = passport_session.post('https://192.168.0.1/passport?mode=auth', data=passport_params, verify=False)
except:
print('Error connecting')
if os.name == 'nt':
os.system("pause")
exit(1)
else:
print('Authentication successful')
passport_cookies = requests.utils.dict_from_cookiejar(passport_session.cookies)
print('cookies=')
print(passport_cookies)
I am not sure that my post request works correctly, so my cookies are empty.
let auth_login = "someName"
let auth_password = "somePassword"
let url = URL(string: "https://192.168.0.1/passport?mode=auth")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
let parameters = [
"from": "passport",
"retpath": "https://192.168.0.1/passport?mode=passport",
"passwd": auth_password,
"login": auth_login,
"display": "page"
]
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard
// let data = data,
let response = response as? HTTPURLResponse,
let fields = response.allHeaderFields as? [String: String]
else {
print("error", error ?? URLError(.badServerResponse))
return
}
let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields, for: url)
print("cookies:\n\(cookies)")
guard (200 ... 299) ~= response.statusCode else {
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
}
task.resume()

Your python code is sending these:
passport_params = {'from': 'passport', 'retpath': 'https://192.168.0.1/passport?mode=passport', 'passwd': auth_password, 'login': auth_login, 'display': 'page'}
as POST data payload.
While your Swift code
let parameters = [
"from": "passport",
"retpath": "https://192.168.0.1/passport?mode=passport",
"passwd": auth_password,
"login": auth_login,
"display": "page"
]
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters)
is doing that as JSON http body.
So these requests are very different, hence the problem you encounter.
Have a look at this answer how to configure POST data in a request in Swift .

Related

Api call to .net C sharp web api project fails in swift but works fine

I am using a asp.net back end with a login end point but no matter what I DO in the swift version of this code I get a 415 when I use it in .net and sharp the api works am not sure what am doing wrong here.
And yes I have enabled transport protocol but its not decoding the jwt token correctly for me in swift
Basically the end point returns the jet token used for accessing the api in an object
let jwtAccessToken: String = ""
let urlString = "http://url.com/login" *** hidden for security
purposes but is correct ****
func CallWebApi()
{
// create the url with URL
let url = URL(string: urlString)! // change server url accordingly
let parameters: [String: Any] = [ "username":
"user1#domain.com", "password": "pass1"]
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded",
forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "Post"
do {
request.httpBody = try
JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
guard
let data = data,
let response = response as? HTTPURLResponse,
error == nil
else {
// check for fundamental networking error
print("error", error ?? URLError(.badServerResponse))
return
}
guard (200 ... 299) ~= response.statusCode else {
// check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
// do whatever you want with the `data`, e.g.:
do {
let responseObject = data
print(responseObject)
} catch {
print(error)
// parsing error
if let responseString = String(data: data, encoding: .utf8) {
print("responseString = \(responseString)")
} else {
print("unable to parse response as string")
}
}
}
task.resume()
}
MyModel is basically a string
import Foundation
class AuthenticationResponse: ObservableObject {
#Published var jwtToken: String
init(jwtToken: String) {
self.jwtToken = jwtToken
}
}
I think 20 years of c sharp in not helping and am doing things it way and not the swift way if someone could advice be great.
Also in csharp we were told its not great in keeping alive the http client as can degrade performance is this the same for swift and if any library's you can recommend makes the code a bit neater the api has swagger docs enabled.
Edit 3
Example response expected back
{
"id": "b181104e-ba3e-4dba-b124-4bb4a3873b17",
"firstName": "user1",
"lastName": "lastname",
"username": "user1lastname#domainname.com",
"playerId": 0,
"jwtToken": "token in is here",//hidden for security
"error": {
"eventName": null,
"errorMessage": null,
"errorDate": null,
"statusCode": null,
"json": null
},
"refreshToken": null
}
I typically send this to the end point from C sharp
{
"username": "user1#domain.com",
"password": "pass1"
}
What I found I had to do was this
let decoder = JSONDecoder()
let responseObject = try
decoder.decode(AuthenticationResponse.self, from: data)
print(responseObject)
And change my class to be off this
import Foundation
struct AuthenticationResponse: Codable {
var jwtToken: String
}
After I done that I got the expected string back but my question is how does one get this to run correctly its completing before I think I need await but also where is it best to stored the jwttoken?

Passing the contents of a text file as JSON to httpBody

FIGURED IT OUT, See below:
I'm trying to create a program that can pass the contents of a text file to a http POST request in swift. I am storing filters for API queries that I am running in the text files, and would like to pass them as JSON objects(? I think, anyway) to request.httpBody in the request. I'm having problems converting the txt files to something that the httpBody can accept as data (json object?).
Here an example txt file. Filters in the same array are combined using OR logic. Arrays of filters are combined using AND logic, so I have to account for both cases.:
zero_RC.txt
{
"query": "Zero Response Code",
"filters": [
{
"filters": [
{
"field": "inventoryState",
"value": "CONFIRMED",
"type": "IN"
},
{
"field": "responseCode",
"value": "0",
"type": "EQ"
},
{
"field": "exception",
"value": "DNS lookup failed",
"type": "EQ"
}]
}]
}
This is the block that I'm trying to get to work. I believe that I need a JSON object, and can pass that to httpBody in the request below. But, still a beginner at this stuff.
// get JSON, somehow
let file = Bundle.main.path(forResource: "zero_RC", ofType: "txt")
let jsonData = file!.data(using: .utf8)
let JSON = try! JSONSerialization.data(withJSONObject: jsonData as Any, options: [])
if JSONSerialization.isValidJSONObject(JSON) {
print("Oh Yeah")
} else {
print("Nah bud, that ain't working")
}
// make the request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
request.httpBody = JSON
So am I taking a string and converting to data, then into a JSON object? I'm thoroughly confused as to how best to do this. I've searched and searched and all I'm finding is parsing articles, which don't exactly help.
Thanks ahead of time.
ANSWERED:
The problem was in the request.setValue. I needed to use Content-Type instead of Accept.
// get JSON
let path = Bundle.main.path(forResource: "zero_RC", ofType: "txt")
let data = try! Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe)
// make the request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
request.httpBody = data
Here's the solution firstly we will decode your json file into a data model, then will encode that data model object to the httpBody.
let path = Bundle.main.path(forResource: "zero_RC", ofType: "txt")
let data = try! Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonDecoder = JSONDecoder()
let json = try! jsonDecoder.decode(JsonObjModel.self, from: data)
// now encoding that jsonData to the http body
let encoder = JSONEncoder()
urlRequest.httpBody = try! encoder.encode(json)
The 'JsonObjModel' will be the following
// MARK: - JSONObjModel
struct JSONObjModel: Codable {
let query: String
let filters: [JSONObjModelFilter]
}
// MARK: - JSONObjModelFilter
struct JSONObjModelFilter: Codable {
let filters: [FilterFilter]
}
// MARK: - FilterFilter
struct FilterFilter: Codable {
let field, value, type: String
}

Passing headers to URL with Swift URLSession

I don't feel like sharing the actual link as it may have some private information so don't be surprised if it doesn't work.
I have a link that looks like this: www.somelink.com/stuff/searchmembers?member=John
And some headers that I need to pass, like Login: Admin, Password: Admin
When I use this site everything seems to be working just fine, I put the link, make it GET and put headers in key:value format and as a result I get the list of all members, but how can I do the same with URLSession? Here's what I currently have and I don't get anything at all. What am I doing wrong there?
func getAllMembers(urlString: String) {
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
request.httpBody = "member=John".data(using: .utf8)!
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(data)
}.resume()
}
Here is What You're Doing Wrong:
Your member=John is actually a URL query parameter. In general, URL requests have query parameters as a part of the URL string itself and not the httpbody.
Quick and Dirty Solution:
You should be good to go if you remove
request.httpBody = "member=John".data(using: .utf8)!
and instead pass the whole "www.somelink.com/stuff/searchmembers?member=John" into your getAllMembers(urlString:) function.
A Better Solution:
Let's say John's username is j o h n. Your function wouldn't make it past that first guard because spaces aren't valid URL string characters.
I like to use URLComponents because it saves me the trouble of having to handle spaces and such.
Here's how I'd write your function:
func getJohnMember(urlString: String) {
//URLComponents to the rescue!
var urlBuilder = URLComponents(string: urlString)
urlBuilder?.queryItems = [
URLQueryItem(name: "member", value: "j o h n")
]
guard let url = urlBuilder?.url else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(String(data: data, encoding: .utf8)) //Try this too!
}.resume()
}
Just to be clear, I would pass just "www.somelink.com/stuff/searchmembers" into the first parameter.
Now if I were to print(url) after the guard let, I'd get
www.somelink.com/stuff/searchmembers?member=j%20o%20h%20n
Much easier this way, no?
That member=John is a URL-query parameter, not part of the request body. So you need to add it to the URL itself.
func getAllMembers(urlString: String) {
guard let url = URL(string: "\(urlString)?member=John") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(data)
}.resume()
}

Alamofire multiple parameters (Query and Form) Swift 4

I'm having a problem sending a POST request with Alamofire.
I need to send the usser and password fields as application/x-www-form-urlencode and also some query data in the url.
I am creating a URLRequest to handle the process, but I'm getting always a 400 response from the server, so I guess the problem must be in the way I create the request.
This is the example in Postman:
I need to send a param in the url and two more in as application/x-www-form-urlencode
Postman 1 - Parameters
Postman 2 - ContentType
I need to do this (that i have in Android)
#FormUrlEncoded
#POST(Constants.AUTH_LDAP)
Call<ResponseBody> authLdap(
#Query(value = Constants.PARAM_REQ, encoded = true) String req,
#Field(Constants.PARAM_LOGIN) String login,
#Field(Constants.PARAM_PASSWORD) String password
);
And this is what I have in swift
let queryParamters = [Constants.Params.PARAM_REQ:req]
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
let fieldParameters = [
Constants.Params.PARAM_LOGIN : user,
Constants.Params.PARAM_PASSWORD : pass]
let url = URL(string: Constants.EndPoints.AUTH_LDAP)
let request = URLRequest(url: url!)
let encoding = try URLEncoding.default.encode(request, with: queryParamters as Parameters)
let encodingpa = try URLEncoding.httpBody.encode(request, with: fieldParameters as Parameters)
var urlRequest = encodingpa
urlRequest.url = encoding.url
urlRequest.allHTTPHeaderFields = headers
Alamofire.request(urlRequest).responseData(completionHandler: { response in
switch response.result {
case .success:
print("sucess")
print(response.response)
case .failure(let error):
print(error)
}
})
Thanks for your help.
Try to create url from queryParameters using URLComponents like
var urlComponents = URLComponents(string: Constants.EndPoints.AUTH_LDAP)!
urlComponents.queryItems = [
URLQueryItem(name: Constants.Params.PARAM_REQ, value: req)
]
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: fieldParameters)
request.allHTTPHeaderFields = headers
Alamofire.request(request).responseJSON { response in
}

Converting CURL command for Locu to Swift

The Locu API provides this example using CURL to perform a location sensitive query:
curl -X POST https://api.locu.com/v2/venue/search -d '{
"api_key" : "f165c0e560d0700288c2f70cf6b26e0c2de0348f",
"fields" : [ "name", "location", "contact" ],
"venue_queries" : [
{
"location" : {
"geo" : {
"$in_lat_lng_radius" : [-37.7750, 122.4183, 5000]
}
}
}]
}'
This is my attempt in Swift:
let LOCU_API_KEY = "<API_KEY>"
let centerLatitude = mapView.region.center.latitude
let centerLongitude = mapView.region.center.longitude
let arr = [centerLatitude,centerLongitude,5000]
let url = NSURL(string: "https://api.locu.com/v2/venue/search")
var request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
var params = ["api_key":LOCU_API_KEY,"fields":["name","location","contact"], "venue_queries":[["location":["geo":["$in_lat_lng_radius":arr]]]]] as [String:AnyObject!]
var err: NSError?
NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
if error == nil {
var err:NSError?
let httpResponse = response as NSHTTPURLResponse!
println(response.description)
if httpResponse.statusCode == 200 {
if var json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error:&err) as? NSDictionary {
println(json)
}
}
}
})
I only get an HTTP 400 - Bad request for this and various simplifications, like just providing the api_key. Version 1_0 of the Locu works fine, although it doesn't have the features I need.
afRequestOperationManager.GET is performing a GET request rather than a POST request.
Furthermore, the request data must be in JSON, whereas you are using URL parameters.
As others have said you need to use .POST or in someway do a POST request not a GET.
Also, it looks like this line:
let urlString = "https://api.locu.com/v2/venue/"
Should be:
let urlString = "https://api.locu.com/v2/venue/search"
Right? Notice the "search" at the end. That is why you are getting 400 I assume (404 I guess?!).
Let us know if it worked.
The '-X POST part of the curl command means you need to do an HTTP POST, not an HTTP GET.
Try using afRequestOperationManager.POST instead.