3

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);

Bartender1382
  • 195
  • 1
  • 10

4 Answers4

0

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
atultw
  • 921
  • 7
  • 15
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

mcscxv
  • 94
  • 4
0

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

Hashim Khan
  • 104
  • 3
  • Sadly not a computer now, but I just had this realization: if I copy and paste the line from my Web server access log, sent by my swift app, it works in a web browser. So that makes me wonder if I’m setting or not setting up some thing that makes Swift send an incompatible format for the headers to be read by my app? – Bartender1382 Mar 25 '22 at 20:57
  • Hi, im unable to access the api route, else i would've fix the code with explanation that might be needed. Can you please make it public? – Hashim Khan Mar 25 '22 at 21:01
  • Not at home so can’t check now. But in my OP I notice I set the line below. It’s from the lesson, maybe that’s the glitch? ‘request.setValue("application/json", forHTTPHeaderField:"Content-Type") request.addValue("application/json", forHTTPHeaderField: "Accept"’ – Bartender1382 Mar 25 '22 at 21:05
  • replace setValue with addValue in application/json Content-type. i've currently my wrapper opened and it has `urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")` – Hashim Khan Mar 25 '22 at 21:10
  • It appears that if I throw my query into the body of my post, it works. I am guessing that I should have used `.dataTask` instead of `.uploadTask` So I need to look for another lesson. How do I mark this question closed? – Bartender1382 Mar 26 '22 at 18:55
  • oh, A question needs to have atleast 2 votes to be closed. If you get any working solution. you can mark it. ps also im upvoting you so it become easy for you. Thank you mister. – Hashim Khan Mar 26 '22 at 18:57
0

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.

Bartender1382
  • 195
  • 1
  • 10