Swift NSURLConnection put HTTP Entity - swift

i want to put a HTTP Entity ( W3.org link ) extra using swift NSURLConnection.
i put a Http Header named entity-body with my urlEncoded key value in NSMutableURLRequest but not working !
Edit
var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
request.HTTPMethod = "POST"
request.setValue("{ my JSON Data }", forHTTPHeaderField: "entity-body")

finally , i found a working answer !
object is my JSON stored in a swift String.
var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
request.setValue(String(count(object)), forHTTPHeaderField: "Content-Length");
request.HTTPBody = object.dataUsingEncoding(NSUTF8StringEncoding);

Related

How can I pass parameters in an HTTP Post request in Swift?

Am working on a simple Swift test app which just calls Perl script on my server. Right now I just want to send over a username and id, and get them back in a JSON response. Nothing more, am still in the learning stage.
But no matter which way I try, I cannot successfully send the two parameters in my URLRequest.
In the sample below, you'll see I try to send them in the main url, I've tried to add them as forHTTPHeaderFields, but the response I get back in my URLSessionDataDelegate is always:
data is {"userid":"","username":""}
JSON Optional({
userid = "";
username = "";
let file = File(link: "http://example.com/cgi-bin/swift.pl?username=John&userid=01", data: "hello")
uploadService.start(file: file)
And within my instance of URLSession I have tried:
// From one of my view controllers I create a File struct
// from a YouTube lesson. Eventually I want to send a file.
// So for now am using just *Hello*:
let uploadTask = UploadTask(file: file)
let url = URL(string: file.link)!
let uploadData = Data(file.data.utf8)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField:"Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("John", forHTTPHeaderField: "username")
request.addValue("01", forHTTPHeaderField: "userid")
uploadTask.task = uploadSession.uploadTask(with: request, from: uploadData)
uploadTask.task?.resume()
Every other part of the Swift test works, I get a response and data in my URSessionDelegate, and no errors. Obviously I just can't figure out how to properly send over the two parameters. For the record:
the Perl script below does work from a linux command line, or when called from a web browser.
If I hardcode the return repsonse in the perl script below, I do recieve it in the my URLSessionDelegate, so I know that I am parsing it correctly
As well, my server's error log shows that $header1 and $header2 never get initialized.
#!/usr/bin/perl -w
use strict;
use CGI;
use JSON;
my $q = new CGI;
my $header1 = $q->param("username");
my $header2 = $q->param("userid");
print $q->header('application/json');
my %out = (username=>"$header1", userid=>"$header2");
my $json = encode_json \%out;
print $json;
exit(0);
You are sending the parameters username and userid as http header values.
Your perl scrip is expecting them a query parameters.
So first create a URLComponents object, than add query items and finally create your url.
Try this:
let uploadTask = UploadTask(file: file)
var urlComponents = URLComponents(string: file.link)!
let queryItems = [URLQueryItem(name: "username", value: "John"),
URLQueryItem(name: "userid", value: "01")]
urlComponents.queryItems = queryItems
let url = urlComponents.url!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField:"Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
uploadTask.task = uploadSession.uploadTask(with: request, from:
uploadData)
uploadTask.task?.resume()
Have a look at this Post that shows how to add query parameters using an extension to URL
In these two lines:
request.addValue("John", forHTTPHeaderField: "username")
request.addValue("01", forHTTPHeaderField: "userid")
You are adding those as http headers and not url query parameters.
To add query parameters, you need to convert to URLComponents first and then convert back: https://developer.apple.com/documentation/foundation/urlcomponents
var urlComponents = URLComponents(string: file.link)!
urlComponents.queryItems = [
URLQueryItem(name: "username", value: "name"),
URLQueryItem(name: "userid", value: "id")
]
let newURL = urlComponents.url!
//use the newURL
Just create a dictionary with data
let parameterDictionary = ["username" : "John", "userid": "01"]
Then create httpBody object using
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameterDictionary, options: []) else { return }
Then simply add that body in your request parameter
request.httpBody = httpBody
I finally found the answer here on StackOverflow.
Having no experience in http methods, the short answer to my question is that if I am using "GET", I would use urlComponents.queryItems, but if I am using "POST" then my parameters would have to be in the http body itself.
But more importantly, the answer found in the link explains when and why you should use "GET" as opposed to "POST", and vice-versa.
So to anyone coming across this, definitely read the answer provided in the link.

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.

My swift HTTP POST request is not being processed properly by a PHP api

I am trying to get a post request to a PHP api. I need to be able to send the request in Json format. The PHP file collects the post request like so:
$postBody = $_POST ['request'];
$signature = $_POST ['signature'];
$rpcRequest = json_decode ( $postBody, true );
I need to build a request that is formatted so the api can read my information. I am building my request like so:
//Here I am building the request as a string. This string is used to get the signature.
var params =
"""
{"method":"phoneNumberVerificationStart","params":{"number":"\(PhoneNumber)"},"id":1}
"""
//here I build the request by using a dictionary.
var jsonParams = ["request": ["method": "phoneNumberVerificationStart","id": 1, "params": ["number": "\(PhoneNumber)"] ]] as NSMutableDictionary
let urlString = "******************************"
//below is just hashing the params into sha256
let hashedParams = sha256(request: params)
let signature = hashedParams.hexEncodedString()
//Take what was just hashed and put it into the signature variable
jsonParams["signature"] = signature
//jsonData takes my built dictionary and turns it into a json format to be sent.
let jsonData = try? JSONSerialization.data(withJSONObject: jsonParams, options: .prettyPrinted)
guard let requestURL = URL(string:urlString) else{return}
let session = URLSession.shared
// Set up the post request to send to the server.
let request = NSMutableURLRequest(url:requestURL)
request.httpMethod = "POST"
// Add the jsonData to the body of the http request. This data is json and when printed out in string form it looks like this:
// ( {"request":{"id":1,"method":"phoneNumberVerificationStart","params":{"number":"**********"}},"signature":"2ebdd87bdc66a04419bfd60e7c9b257039bf66dacd1623a1995c971e7cb68ed6"}
//For some odd reason Id shifts up to the front of the json file?
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
print(String(data: request.httpBody!, encoding: .utf8)!)
//After this I send the request the server does not understand my post request
let task = session.dataTask(with: request as URLRequest){
(data,respone, error) in
if error != nil {
print(error?.localizedDescription)
//print(String(data:myData!,encoding: .utf8)!)
}
do{
print (String(data: data!, encoding: .utf8)!)
}
I am thinking my problem is the request not being sent as a json object but rather raw data. I am receiving an error from the server that it cannot find the fields 'request' or 'signature'.

swift: can't set authorization header in http request

let URL: Foundation.URL = Foundation.URL(string: "MYURL")!
let request = NSMutableURLRequest(url:URL)
request.httpMethod = "GET"
request.setValue("8be4967f-dbc4-42f9-982a-30a25d9d5e12", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
As you see both Content-type and Authorization headers are set, but when server receives it, there is no Authorization in header.
But if I change Authorization word to any other random word, it works fine. (the problem consists even with Alamofire) any idea how to solve this?