HTTP Request swift providing parameters - swift

For a simple iOS (swift) application for my university I try to login to one of their pages to retrieve the amount of money currently on my card. However when doing my http request I can't get the data I need.
This is my code:
let url = NSURL(string: "https://campuscard.hhs.nl/portal/j_spring_security_check")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let data : NSData = ("?j_username=USERNAME&j_password=PASSWORD").dataUsingEncoding(NSUTF8StringEncoding)!;
request.HTTPBody = data;
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
It gives me the error that I have enterred the wrong credentials and when I print my request it says:
<NSMutableURLRequest: 0x7f8d7b53bd30> { URL: https://campuscard.hhs.nl/portal/j_spring_security_check, headers: {
"Content-Type" = "application/x-www-form-urlencoded";
} }
So I think it doesn't include the username and password.
Does anyone have an idea?
It would be much appreciated by me and other students on my university!
added
Me and a friend of my class we see the attributes in the request through Charles thanks to you, however since we both never tried working with this we don't know how to handle those attributes. We simply added all we can find to the request and tried it but we still get the ArrayOutOfBoundsException on the server.
var dataString = "j_username=USERNAME&j_password=PASSWORD"
var request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: "https://campuscard.hhs.nl/portal/j_spring_security_check")
var postString = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
//request.setValue("JSESSIONID=C78C688403A836968EC1FEAED9AE9126", forHTTPHeaderField: "Cookie")
request.setValue("campuscard.hhs.nl", forHTTPHeaderField: "Host");
request.setValue("keep-alive", forHTTPHeaderField: "Connection");
request.setValue("41", forHTTPHeaderField: "Content-Length");
request.setValue("max-age=0", forHTTPHeaderField: "Cache-Controle");
request.setValue("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", forHTTPHeaderField: "Accept");
request.setValue("https://campuscard.hhs.nl", forHTTPHeaderField: "Origin");
request.setValue("https://campuscard.hhs.nl/portal/login", forHTTPHeaderField: "Referer");
request.setValue("gzip,deflate", forHTTPHeaderField: "Accept-Encoding");
request.setValue("nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4", forHTTPHeaderField: "Accept-Language");
request.HTTPBody = postString
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
I'm sorry to place such a big piece of code on you, but maybe there is something you can see is wrong. Thank you for your time

The body of the x-www-form-urlencoded request should not contain the ?.
As an aside, you should be percent encoding USERNAME and PASSWORD. Right now, if either (more likely, the password) contained certain reserved characters, your request would fail. I use a extension like this in Swift 2:
extension String {
/// Percent escape value to be added to a HTTP request
///
/// This percent-escapes all characters besize the alphanumeric character set and "-", ".", "_", and "*".
/// This will also replace spaces with the "+" character as outlined in the application/x-www-form-urlencoded spec:
///
/// http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
///
/// - returns: Return percent escaped string.
func stringByAddingPercentEncodingForFormUrlencoded() -> String? {
let allowedCharacters = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._* ")
return stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacters)?.stringByReplacingOccurrencesOfString(" ", withString: "+")
}
}
I use this stringByAddingPercentEncodingForFormUrlencoded function on the USERNAME and PASSWORD values (but not the whole string).
Or, in Swift 3:
extension String {
/// Percent escape value to be added to a HTTP request
///
/// This percent-escapes all characters besize the alphanumeric character set and "-", ".", "_", and "*".
/// This will also replace spaces with the "+" character as outlined in the application/x-www-form-urlencoded spec:
///
/// http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
///
/// - returns: Return percent escaped string.
func addingPercentEncodingForFormUrlencoded() -> String? {
let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._* ")
return addingPercentEncoding(withAllowedCharacters: allowedCharacters)?.replacingOccurrences(of: " ", with: "+")
}
}
The absence of the username and password when examining NSURLRequest is not at all worrying (I wouldn't have expected it to include the body of the request when you log it like that). If you want to check, run this through Charles or something like that.
If you're using Charles, if you want to inspect HTTPS interaction, you have to enable SSL proxying, and add your domain to the list of location. See "Proxy settings..." on "Proxy" menu, and click on the "SSL" tab. See Charles Web Debugging Proxy.
This will show you the full request in all of its glory. If you're trying to have your app log on like you would from a web browser, you can use Charles to watch the web browser exchange and compare and contrast that to your app.
In your revised question, you are now showing all the various headers that you're trying to set. (You don't have to set some of these: Watch existing app request in Charles and you'll see some of these are already set.) I'd be surprised if any of these are needed.
Ironically, the only one that's probably critical is the one you've commented out, JSESSIONID. lol. Many of these web sites will provide some session ID in the login HTML. Then when you then try to submit the login request, you have to pass the same JSESSIONID that was provided to you by the login HTML page.
So the model is usually (a) get the login page; (b) parse it for whatever header fields that need to be set in subsequent requests (e.g. looks like it might be JSESSIONID, on the basis of your example); and (c) supply that session id for all subsequent requests.
This is supposition, as I haven't been able to actually see the full conversation b/w the web browser and your particular web server, but this is the sort of pattern I've seen before. Just watch web browser requests/responses, paying special attention to cryptic id numbers buried in the HTML that might be provided in subsequent requests (either in the body or the headers).

Related

Why response is always {"detail":"Unsupported media type \"text/plain\" in request."} in swift?

I have created a sample app in Django which deletes a question from App. And provides a correct output when consumed using POSTMAN.
class Questions(APIView):
def delete(self,request):
received_id = request.POST["id"]
print(received_id)
place = Question.objects.get(pk=received_id)
place.delete()
questions = Question.objects.all()
seriliazer = QuestionSerializer(questions,many = True)
return Response({'Orgs': seriliazer.data})
However, when I am trying to achieve it from iOS app, it's returning {"detail":"Unsupported media type "text/plain" in request."}
func deleteQuestion( id: Int){
guard let url = URL(string: "http://127.0.0.1:8000/V1/API/questions/") else {
return
}
var request = URLRequest(url: url)
let postString = "id=15"
request.httpBody = postString.data(using: String.Encoding.utf8);
request.httpMethod = "DELETE"
URLSession.shared.dataTask(with: request) { data, response, error in
let str = String(decoding: data!, as: UTF8.self)
print(str)
if error == nil {
self.fetcOrganizatinData()
}
}.resume()
}
Could not really understand where exactly the problem is ?
If the api is expecting Json, the body you are sending is not Json, it’s encoded plain text. If it should be Json you can change the body string into the Json format like:
“{\”id\”:15}”
// you may want to tell it what you’re sending
request.setValue("application/json", forHTTPHeaderField: "Accept-Encoding")
Another thing it could be is the request is missing the Accept-Encoding header which tells the api what you’re sending up where Content-Type is what the api typically sends down.
I’ve experienced header injection when I’ve sent requests through specific gateways that aren’t always right. I’d the header isn’t present, something along the way could try to help you out and add the header. This has caused me problems on the past. I still don’t know exactly where in our stack it was occurring, but adding the header fixed my problem.
You can add the header like:
request.setValue("charset=utf-8", forHTTPHeaderField: "Accept-Encoding")
DELETE request's body will be ignored, I could guess from the Is an entity body allowed for an HTTP DELETE request? post. HENCE Better to send the complete URL or in header itself,
so I made the function as below
def delete(self,request):
received_id = request.headers['id']
place = Question.objects.get(pk=received_id)
place.delete()
return HttpResponse("DELETE view is working fine ")
and swift
func deleteQuestion( id: Int){
guard let url = URL(string: "http://127.0.0.1:8000/V1/API/questions/") else {
return
}
var request = URLRequest(url: url)
//let postString = "id=\(id)"
// request.httpBody = postString.data(using: String.Encoding.utf8);
request.httpMethod = "DELETE"
request.setValue("charset=utf-8", forHTTPHeaderField: "Accept-Encoding")
request.setValue("charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("\(id)", forHTTPHeaderField: "id")
URLSession.shared.dataTask(with: request) { data, response, error in
let str = String(decoding: data!, as: UTF8.self)
print(str)
if error == nil {
self.fetcOrganizatinData()
}
}.resume()
}
Shortly add Content-Type application/json in your headers
Reason
this happens because the postman has some default headers usually 8.
One of them is
Content-Type text/plain
and by writing "Content-Type": "application/json" we can overwrite that rule.
So whenever you want to pass your data like JSON do that.
to learn more what is by default in postman
I recommend you to read this official documentation of postman.
It happens with me I solved this with overwriting default Content-Type

Phaxio API Create Fax and using the dreaded multipart/form-data encoding type

I'm slowing building out a Swift interface to the Phaxio set of API's. I have figured out some of the basic GET functions but the hard one is the create fax function. It's my first attempt at creating a multipart/form-data POST and uploading a binary file to the fax server. I think I'm close. I'm struggling with correctly passing my credentials, and fax number while also including the file in a multipart format. Just now, I was able to work through the "bad Message" response I was getting back from the server for the file upload. Now I'm stuck on the error below. Any help would be appreciated.
import Cocoa
// This program uses the phaxio API to send a fax (at least thats the intent). Shout out to Ignacio Nieto Carvajal's very helpful Networking in Swift: The Complete Guide on www.digialleaves.com to help me piece this together. //
// handy function to create unique identifier to define the multi-part boundaries
func generateBoundaryString() -> String {
return "Boundary-\(UUID().uuidString)"
}
var body = Data()
var parameter = ""
// Open file, read contents into buffer - This works great, so easy in SWIFT!
let fileMgr = FileManager.default
var sourceFile = "/tmp/test.pdf"
let databuffer = fileMgr.contents(atPath: sourceFile)
// post the user, pass and faxnumber
let boundary = generateBoundaryString()
parameter.append("--\(boundary)\r\n")
parameter.append(contentsOf: "Content-Disposition: form-data; name=\"api_key\"; value=\"cn577fcvrcrjuj9v8\"\r\n")
parameter.append("--\(boundary)\r\n")
parameter.append(contentsOf: "Content-Disposition: form-data; name=\"api_secret\"; value=\"ciwx0sc7owqv4gzg\"\r\n")
parameter.append("--\(boundary)\r\n")
parameter.append(contentsOf: "Content-Disposition: form-data; name=\"to\"; value=\"5555555555\"\r\n")
parameter.append("--\(boundary)\r\n")
parameter.append(contentsOf: "Content-Disposition: form-data; name=\"file\"; filename=\"\(sourceFile)\"\r\n")
parameter.append(contentsOf: "Content-Type: application/PDF\r\n\r\n")
// Create boundary around file contents
body.append(contentsOf: parameter.utf8)
// Add binary contents of file
body.append((databuffer ?? nil)!)
body.append(contentsOf: "\r\n".utf8)
body.append(contentsOf: "--\(boundary)--\r\n".utf8)
// Initialize our URL & Request
let baseURL = "https://api.phaxio.com/v2/faxes"
let session = URLSession.shared
let url = URL(string: baseURL)!
var request = URLRequest(url: url)
//Define request method & set header values
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
// Initialize HTTP Request Body
request.httpBody = body
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("error took Place\(error)")
return
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("response data string: \(dataString)")
}
}.resume()
This is the Response I'm getting from the Server:
{"success":false, "message": "You must provide API credentials for this operation (HTTP auth or api_key and api_secret)."}
Solved! Believe it or not, by appending the credentials API_KEY, API_SECRET and the phone number to the URL string, Phaxio accepted the message and queued the fax for sending.

pho.to API Request Failing in Swift

Im currently trying to work with the pho.to API in my iOS application. I am experimenting with making simple requests according to the documentation, however I cannot seem to get the request to go through successfully. Inside my API client file, I have this code:
let dataStr = """
<image_process_call>
<image_url>http://developers.pho.to/img/girl.jpg</image_url>
<methods_list>
<method order="1">
<name>desaturation</name>
</method>
<method order="2">
<name>caricature</name>
<params>type=1;crop_portrait=true</params>
</method>
</methods_list>
<thumb1_size>100</thumb1_size>
</image_process_call>
"""
let encodedStr = dataStr.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
let signData = encodedStr.hmac(key: key)
let urlStr = "https://opeapi.ws.pho.to/addtask/?app_id=\(appId)&key=\(key)&sign_data=\(signData)&data=\(encodedStr.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!))"
The HMAC encoding is being done according to this Stack Overflow post. Unfortunately when making a request to this URL using URLSession I get this response:
<?xml version=\"1.0\"?>\n<image_process_response><status>SecurityError</status><err_code>614</err_code><description>Error in POST parameters: one or more parameters (DATA , SIGN_DATA or APP_ID) are empty</description></image_process_response>
I feel like my issue is more related to actually forming the request rather than something specific to the API itself. I know my code is a little messy, however I was hoping that somebody could point me in the right direction in terms of making a request like this. Thanks!
As per their documentation you can see that data sent over from POST requests are in body (In cURL calls -d specifies the body of the request)
You are sending params/data in query, which the pho.to API doesn't accept, hence the error.
Here's a sample on how you can do:
let defaultSessionConfiguration = URLSessionConfiguration.default
let defaultSession = URLSession(configuration: defaultSessionConfiguration)
// Setup the request with URL
let url = URL(string: "https://opeapi.ws.pho.to/addtask")!
var urlRequest = URLRequest(url: url)
// Convert POST string parameters to data using UTF8 Encoding
let postData = yourXMLString.data(using: .utf8)
// Set the httpMethod and assign httpBody
urlRequest.httpMethod = "POST"
urlRequest.httpBody = postData
// Create dataTask
let dataTask = defaultSession.dataTask(with: urlRequest) { (data, response, error) in
// Handle your response here
}
// Fire the request
dataTask.resume()

UIApplication.shared.open Add Header Data

I need to open an external link in Safari to a page but the server I'm hitting with the url requires that I need to verify the IP address in a header. How can I pass header data in with UIApplication.shared.open?
There's an option parameter but I can't find any examples or documentation if this is the correct parameter to use to pass header data like ['header': _ipAddress].
Below is an example of where I'm setting headers for another kind of request that just opens in the app itself but I don't know how I can do this for UIApplication.shared.open.
let newRequest = (self.request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
if let _ip = EDataManager.shared.ipAddressOfTheUser, _ip.length() > 0 {
newRequest.setValue(_ip, forHTTPHeaderField: "ex_header")
}
URLProtocol.setProperty("true", forKey: EWebViewAssetDownloadProtocol.CustomKey, in: newRequest)
let defaultConfigObj = URLSessionConfiguration.default
let defaultSession = URLSession(configuration: defaultConfigObj, delegate: self, delegateQueue: nil)
self.dataTask = defaultSession.dataTask(with: newRequest as URLRequest)
self.dataTask!.resume()
Unfortunately this is not supported for UIApplication.openURL(:options:completion:). Additionally, you can't open a URLRequest out to another application, so there really is no way to pass header fields into an external link to open in Safari.
If you have control of the external API, the best way to handle this is to pass URL query parameters into the url you open. However you have to be careful with that, because URL query parameters are viewable by users, so you can't pass any sensitive data that way.

How to send '&' character in HTTP request body?

I have these parameters saved as string :
title=Culture, sport & recreation (1 day)&useremail=ammar#gmail.com&days=2&ispredefined=false&languageid=1&websiteid=1&node=item&languageid=1&websiteid=1&moduletype=Accommodation&moduleuniquename=abu.dhabi.plaza.hotel.apartments&dayindex=1
and I am passing it to a request body using the below code:
let request = NSMutableURLRequest(URL: URL.getURL(url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.HTTPBody = params.dataUsingEncoding(NSASCIIStringEncoding)
but the problem is that the title in the string title=Culture, sport & recreation has '&'character and i am getting 400 bad Request from the server
please any advice how to solve this problem?
As you can read in the attached links you have to encode the ampersand sign.
encodeURIComponent('&') gives "%26"
Thus, you have to replace all ampersands with %26 or use encodeURIComponent(str) in your code.
escaping ampersand in url
How can I send the "&" (ampersand) character via AJAX?