swift: can't set authorization header in http request - swift

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?

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

How to call a post web service with UrlSession.DataTaskPublisher?

I have a simple question that I can see only dataTaskPublisher in documentation with which I was able to call a get web service but how can I call a post web service which can return a publisher?
you can define a request and call it in your dataTaskPublisher like this :
var request = URLRequest(url: URL(string: "url")!)
let session = URLSession.shared
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = try! JSONSerialization.dataWithJSONObject(parameters, options: [])
session.dataTaskPublisher(for: request)
Use constructor with URLRequest and prepare request with any HTTP method you need.
/// Returns a publisher that wraps a URL session data task for a given URL request.
///
/// The publisher publishes data when the task completes, or terminates if the task fails with an error.
/// - Parameter request: The URL request for which to create a data task.
/// - Returns: A publisher that wraps a data task for the URL request.
public func dataTaskPublisher(for request: URLRequest) -> URLSession.DataTaskPublisher

Swift NSURLConnection put HTTP Entity

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

HTTP Request swift providing parameters

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