I read this question but it didn't answer my question.
To me Headers and Parameters are both dictionaries with the difference that headers is [String : String] while Parameters is [String : AnyObject]? and so if your parameters are also Strings then you could send them within the headers (while using a 'x-' prefix to signify they aren't standard headers) which is a common but not good practice.
Is that correct?
Are there other difference between headers and parameters?
What kind of other non-String types would you be sending using parameters?
Alamofire Request method
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request
{
return Manager.sharedInstance.request(
method,
URLString,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
As an example I have seen people passing ["x-ios-version" : UIDevice.currentDevice().systemVersion] or build versions through headers
The accepted answer is very practical. Make sure you see it. But there are two foundational differences I will discuss in depth:
Where header and parameters are placed in an HTTP Request
A URL is different from an HTTP Message. An HTTP Message can either be a Request or a Response. In this answer I will focus on the request.
An HTTP Request is made up of mainly the url, http-method, http-headers (there are other chunks in it, but I'm just mentioning the ones we care about the most)
Request = Request-Line ; Section 5.1
*(( general-header ; Section 4.5
| request-header ; Section 5.3
| entity-header ) CRLF) ; Section 7.1
CRLF
[ message-body ] ; Section 4.3
A request line is:
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
CLRF is something like a new line.
For more see here and here. You might have to do some back and forth between the links til you get it right. If you really wanted to go deep then see see this RFC
So basically a request is something like:
POST /cgi-bin/process.cgi?tag=networking&order=newest HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Content-Type: text/xml; charset=utf-8
Content-Length: 60
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
first=Zara&last=Ali
The query params are within the URL. HTTP Headers are NOT part of the URL. They're part of the HTTP Message. In the above example, query params is tag=networking&order=newest, the headers are:
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Content-Type: text/xml; charset=utf-8
Content-Length: 60
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
So when you make a network request, you're sending a structured STRING using the http protocol. That string is sent through a TCP connection.
2 - Why and where one is preferred over the other
From discussion with Rob in chat:
The criteria is that if it's information about the request or about the client, then the header is appropriate.
But if it's the content of the request itself (e.g. what you are requesting from the server, some details that identify the item to be returned, some details to be saved on the web server, etc.), then it's a parameter e.g.:
Parameter
Let's say you're requesting an image for a product. The product id may be one parameter. The image size (thumbnail vs full size) might be another parameter.
The product id and requested image size are examples of "some detail" (or parameter) being supplied as part of the content of a request.
Header
But things like the request is JSON or x-www-form-urlencoded are not the content of the request, but meta data about the request (especially since it's necessary for web service to know how to parse the body of the request). That's why it's a header.
Most likely if your app makes various requests, its headers would have a lot in common. However the parameters due to the fact that they are content based should be more varied.
Construction using URLComponents
class UnsplashRequester {
static let session = URLSession.shared
static let host = "api.unsplash.com"
static let photosPath = "/photos"
static let accessKey = "My_access_key"
static func imageRequester(pageValue: String, completion: #escaping (Data?) -> Void) {
var components = URLComponents()
components.scheme = "https"
components.host = host
components.path = photosPath
// A: Adding a Query Parameter to a URL
components.queryItems = [URLQueryItem(name: "page", value: pageValue)]
let headers: [String: String] = ["Authorization": "Client-ID \(accessKey)"]
var request = URLRequest(url: components.url!)
for header in headers {
// B: Adding a Header to a URL
request.addValue(header.value, forHTTPHeaderField: header.key)
}
let task = session.dataTask(with: request) { data, _, error in
}
}
}
Here is the list of differences:
They are designed for different purposes. Headers carry meta info, parameters carry actual data.
HTTP Servers will automatically un-escape/decode parameter names/values. This does not apply to header names/values.
Header names/values need to be manually escaped/encoded at client side and be manually un-escaped/decoded at server side. Base64 encoding or percent escape is often used.
Parameters can be seen by end-users (in URL), but headers are hidden to end-users.
Related
I am trying to access Spotify's web API. I am currently using Alamofire to request the search operation which only requires a token. However, I could not figure out why it won't let me send multiple arguments.
let headers = ["Authorization": "Bearer {your access token}"]
var searchURL = "https://api.spotify.com/v1/search?q=Odesza&type=track"
AF.request(.GET, url, headers: headers)
.responseJSON { response in
debugPrint(response)
}
Alamofire does a great job of formatting parameters to a request url. Just as you pass parameters with the .post method to the AF request function you also pass a parameters [string : any] to the AF request for .get methods as well. The difference is .post will put the parameters in the request.body/data vs. .get will format the parameters into the url with the ?q=my_search_string
something along the lines of this:
Let params: [string :any] = ["q": "odesza", "type":"track"]
AF.request(.GET, url, parameters:params, headers: headers) .responseJSON { response in debugPrint(response
I'm trying to login to a web service as follows:
func Login(completionHandler:#escaping (Bool) -> ()) {
let url = MyUrl
let parameters: Parameters = [
"Password":password,
"StayLoggedIn":NSNumber(value: true),
"UserName":username
]
var headers:HTTPHeaders = commonHeaders()
headers["Content-Type"] = "application/json;charset=UTF-8"
Alamofire.request(url, method:.post, parameters:parameters, headers:headers).responseJSON { response in
switch response.result {
case .success:
debugPrint(response)
case .failure(let error):
print(error)
}
}
}
Somehow, the server returns a 501 error.
I'm in the process of rewriting an Obj-c app in Swift, and replacing NSURLSessions with Alamofire into the bargain.
What I see in my working obj-c app, is that the headers sent to the server contain
Content-Type: application/json;charset=UTF-8
As you can see, I explicitly add this header to my request, but somehow, it is not sent to the server.
What I also see, is that my login credentials are not sent to the server.
So, I guess my question is: how do I tell Alamofire to use the correct encoding/content type?
try this encoding application/x-www-form-urlencoded instead of application/json;charset=UTF-8
That's the code I'm using to make POST request to my Flask server in localhost:
func data_request() {
let url:NSURL = NSURL(string: "http://192.168.1.192:9880/api/register")!
Alamofire.request(.POST, url, parameters: ["login":"login", "password" : "12345"]).responseJSON { response in
switch response.result {
case .Success:
NSLog("Validation Successful")
case .Failure(let error):
NSLog("\(error), \(String(data: response.data!, encoding: NSUTF8StringEncoding))")
return
}
if (response.result.value as? [String: AnyObject]) != nil{
print(response.result.value)
}
}
}
But it sends GET request! Both server and local proxy tell it was GET request - that's what Burp has intercepted:
GET /api/register/ HTTP/1.1
Host: 192.168.1.192:9880
Accept: */*
User-Agent: Project Manager/Roman-Nikitin.Project-Manager (1; OS X 10.11.3)
Accept-Language: en;q=1.0, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5
Accept-Encoding: gzip;q=1.0, compress;q=0.5
Connection: close
I had the same problem, you just have to put / at the end of URL. Problem is in Alamofire, I think. It works weird with normal server redirections from www.domain.com/something to www.domain.com/something/
For anyone getting here because they have the same problem (like me): In my case, I already had a / at the end and I had to remove it.
Based on this answer, it seems to be the server redirecting the original POST request to a GET request. So either figure out what your server requires (trailing slash or no), or set up the server like in the linked answer.
I am trying to connect to a local node.js server setup and authenticate the user. I keep getting the 500 status code and can't figure out what I am missing.
I have tried hitting the server with these credentials from a web browser, and it works as expected.
Note: I do understand I have to use the NSURLSession instead of NSURLConnection, but for now, I need to get this to work.
Here is my code,
func signInUserWithDetails(userName:String,userPassword:String,serverURL:NSURL) {
let credDic :[String:String]=["user[name]":userName,
"user[password]":userPassword ]
self.httpMethod="PUT"
self.httpPath="/account"
self.expectedStatusCode=201
self.actualStatusCode=NSNotFound
self.requestUniqueIdentifier = NSUUID().UUIDString
let urlComponents = NSURLComponents(URL: serverURL, resolvingAgainstBaseURL: false)!
urlComponents.path = httpPath
let formedURL = urlComponents.URL!
var requestOrg = NSMutableURLRequest(URL: formedURL)
requestOrg.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
requestOrg.addValue("application/json", forHTTPHeaderField: "Accept")
requestOrg.HTTPMethod=self.httpMethod!
print(requestOrg.allHTTPHeaderFields) // Output 1
do{
let theJSONData = try NSJSONSerialization.dataWithJSONObject(credDic,options: NSJSONWritingOptions.PrettyPrinted)
let theJSONText = NSString(data: theJSONData,encoding: NSASCIIStringEncoding)
requestOrg.HTTPBody = theJSONData;
let tempD=try NSJSONSerialization.JSONObjectWithData(requestOrg.HTTPBody!, options: []) as? [String:String]
print("\(tempD)") //Output 2
}catch let error as NSError {
print(error)
}
connection = NSURLConnection(request: requestOrg, delegate: self, startImmediately: true)!
}
And I am just printing out the response with this,
func connection(didReceiveResponse: NSURLConnection, didReceiveResponse response: NSURLResponse) {
print("----------------------didReceiveResponse")
self.response=response
print("Response Received:"+"\(self.response)")
let urlResponse:NSHTTPURLResponse = response as! NSHTTPURLResponse
let responseCode=urlResponse.statusCode
self.actualStatusCode=responseCode
}
And the result I get is
Optional(["Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded"])
Optional(["user[password]": "R", "user[name]": "R"])
----------------------didReceiveResponse
Response Received:Optional(<NSHTTPURLResponse: 0x7faba269d440> { URL: http://localhost:3000/account } { status code: 500, headers {
Connection = "keep-alive";
"Content-Length" = 1464;
"Content-Type" = "application/json";
Date = "Sat, 26 Dec 2015 08:34:45 GMT";
"X-Powered-By" = Express;
} })
And the didReceiveData throws this error
{"error":{"message":"Cannot read property 'name' of undefined","stack":"TypeError: Cannot read property 'name' of undefined\n at Object.exports.signIn [as handle] ( .......
Status code 500 means, that the server could not process your data and ran into an internal error. This oftentimes is caused by improperly encoded HTTP messages, where the server was unable to catch all possible errors.
When looking at your code, it becomes immediately apparent:
You are not sending a properly application/x-www-form-urlencoded encoded data to the server. This is likely the main cause of your problem. The other cause might be, that it's likely not a PUT but a POST method which is required to sign-in.
But before explaining how you encode your data properly, I would suggest to find out whether your server accepts JSON as content data (application/json). If so, properly encoding the data is much easier: having a JSON object (your variable credDic), simply convert it to JSON as UTF-8 in a NSData container. Then, get the length in bytes, set headers Content-Type and Content-Length accordingly.
I had a similar issue but after tried to include Content-Type using application/json, it was solved.
Example: request.addValue("application/json", forHTTPHeaderField: "Content-Type")
The client application gets an HTTP status code of 500 with the message "Internal Server Error" as a response for API calls. The 500 Internal Server error could be caused by an error during the execution of any policy within Edge or by an error on the target/backend server.
The HTTP status code 500 is a generic error response. It means that the server encountered an unexpected condition that prevented it from fulfilling the request. This error is usually returned by the server when no other error code is suitable
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).