What is the difference between the two request methods of alamofire? - ios

I was just playing with Alamofire framework and making few api calls. However I observed there are two request methods in alamofire :
public func request(method: Method, URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil) -> Request{...}
and
public func request(URLRequest: URLRequestConvertible) -> Request {...}
I found this really interesting, because the first method prototype is detailed and is easily understood. But the second one is quite confusing, I know that it takes a parameter which conforms to URLRequestConvertible protocol that is defined by Alamofire.
In the second request prototype the HTTP Method (GET or POST) that needs to be used is never specified, so how does the alamofire knows which HTTP method to be used. Is there a way to let alamofire know which http method to use while making request.
Also what are the other significant differences between these two methods (if any) and which one is preffered and why?
Thank you.

The rendition of request without a method or parameters is presuming that you manually prepared the NSMutableURLRequest that includes the appropriate HTTPMethod, the HTTP headers (e.g. Content-Type, etc.), and HTTPBody.
You wouldn't generally use this rendition of the request method (we use Alamofire precisely to get us out of the weeds of manually constructing requests), but is useful when you have to construct a request that Alamofire cannot otherwise prepare (e.g. Sending json array via Alamofire).

Related

How to send `apikey` in header in Alamofire 4.5,Swift 4?

I want to make a HTTP post request via Alamofire 4.5. The request need an authorization header(which is a Api key). But whenever I fired the request,my server cant detect the ApiKey.'
Here is how I make the Alamofire request
let params : [String : Any] =["param1":param1,"param2":param2]
let headers : HTTPHeaders = ["authorization" : apiKey]
Alamofire.request(MY_URL, method: .post, parameters: params, headers: headers).responseJSON {
response in
switch response.result{
case .success(let result):
//other code here
}
I triple checked the value of apiKey ,the value is correct,but the request sent,my server cant detect the authorization at all.
I totally no idea whether I do anything wrong here,cause I very new in Swift.Kindly provide a proper solution.Thanks
Edit :
In my server code,I using Slim 2
$app->map('/MY_URL','authenticate',function ()use($app){
}
'authenticate' is the point that scan for the authorization: apiKey in the headers,so now the problem is my server cant get the value of apiKey therefore always giving the same error "Api Key is missing" which I set when no Api Key found.
I tried the method below in Alamofire Documentation,but the result still the same.
What I tried:
let headers: HTTPHeaders = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Accept": "application/json"
]
Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
What I missing here?Somebody please give me some hints to do it..Thank you.
EDIT:
To be more clear on my I mean for authorization : apiKey I show the way I make request in Postman.
Normally I just insert the "authorization": apiKey in the Headers in the request
but in Swift,the web service cant get the value of apiKey,therefore the server always return this following response :
{
"error": true,
"message": "Api key is missing"
}
This is working fine for me with Alamofire 4.6.0
let url = "WEB API URL"
let headers = [
"Content-Type":"application/x-www-form-urlencoded",
"authorization" : "apiKey"
]
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
let params : [String : Any] = ["param1":param1,"param2":param2]
Alamofire.request(url, method: .post, parameters: params as? Parameters, encoding: URLEncoding.httpBody, headers: headers).responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON)")
}else{
print("Request failed with error: ",response.result.error ?? "Description not available :(")
}
}
TLDR;
The problem is that iOS's URLRequest automatically capitalize headers. At the same time you API does not follow best practices.
Change your API to comply to RFC 7230 and allow it to accept headers case-insensitively.
The whole story:
At first, your question seemed a bit odd since there is no obviously wrong code in what you provided. Nevertheless I tried to reproduce your request in Postman.
Now we should stop and I must warn you to never post what you did in your "Here is my request" section. The information given there allowed me to completely reproduce your request in Postman (including headers and exact fields' names and values), which is good to solve your problem. But at the same time you shared your presumably private and maybe even bought API key to everyone who see your question. Which is obviously not good and I would recommend you to change your API key if it is possible.
Then I tried your code and noticed exactly the same behavior you talking about. I debugged responseJSON closure and observed response.request?.allHTTPHeaderFields property:
(lldb) po response.request?.allHTTPHeaderFields
▿ Optional<Dictionary<String, String>>
▿ some : 2 elements
▿ 0 : 2 elements
- key : "Content-Type"
- value : "application/x-www-form-urlencoded; charset=utf-8"
▿ 1 : 2 elements
- key : "Authorization"
- value : "f8f99f9506d14f0590863d5883aaac9b"
(if you don't understand what I wrote read about debugging in xcode and in particular for lldb's po command)
As you can see, authorization header's name start with a capital A letter even though I passed it all lowercased.
I tried to send new request with postman with capital A and yes - I learned that your API accepts only lower-cased authorization header name.
"It isn't really a problem" you think right now. "We should just change our authorization header name somewhere and it should be just fine, right?"
NOT SO EASY.
I tried a few things which all lead me to the URLRequest's setValue(_:forHTTPHeaderField:) method. Alamofire calls it and I tried it too. Surprisingly enough after calling this method "authorization" header always changes to "Authorization". Then I found the thing that particularly interesting for us:
Note that, in keeping with the HTTP RFC, HTTP header field names are case-insensitive.
Keep in mind that I even tried to change URLRequest's allHTTPHeaderFields directly. Had the same result.
Which leads us to the following conclusion: Apple intentionally ignores input headers' case and very irresponsibly changes it (again intentionally since it takes at least a few lines of code somewhere instead of just plugging given headers directly into request). As of now I know no possible solution to this problem (if we want to classify it as a problem which is a bit controversial). Search says that is exists from earlier days of iOS (http://0xced.blogspot.com.by/2010/06/fixing-nsmutableurlrequest.html). You could call some private objective-c APIs which could help, but in fact you'll get unstable or undefined behavior and would likely get rejected from App Store.
So my conclusion, and probably the only right choice in this situation is to change your API.
Configuration is optional, the only thing you need is to setup request right. Make sure (double sure) that you format your auth correctly.
In some (not that rare cases this should be formatted like this:
["Authorization": "Bearer <#your_token#>"]
And what I found about Slim 2 it's also with Bearer so maybe you missing this.
https://github.com/dyorg/slim-token-authentication/tree/master/example#making-authentication-via-header
Example from this:
$ curl -i http://localhost/slim-token-authentication/example/restrict -H "Authorization: Bearer usertokensecret"
With this, you can also check if this working with simple curl command. It should. If not, there is definitely a problem with fields you're sending rather than Alamofire itself.
In docs for Alamofire you can find:
/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
/// `method`, `parameters`, `encoding` and `headers`.
///
/// - parameter url: The URL.
/// - parameter method: The HTTP method. `.get` by default.
/// - parameter parameters: The parameters. `nil` by default.
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
/// - parameter headers: The HTTP headers. `nil` by default.
///
/// - returns: The created `DataRequest`.
public func request(_ url: URLConvertible, method: Alamofire.HTTPMethod = default, parameters: Parameters? = default, encoding: ParameterEncoding = default, headers: HTTPHeaders? = default) -> Alamofire.DataRequest
Here is an example:
Alamofire.request("https://...",
method: .get,
parameters: ["myKey1": "myValue1"],
encoding: JSONEncoding.default,
headers: self.authHeader).responseJSON { response in
//your response
}

What's the counterpart of AFJSONRequestSerializer in Alamofire?

I need to serialize a Request with some default headers and an authentication token. With AFNetworking, I would create a subclass of AFJSONRequestSerializer. What can I do in Alamofire? I've read about response serializers but not request serializers.
Thanks.
In alamofire you just need to specify the headers you need before creating the request.
Something like this should work:
var authenticatedHeaders = ["Authorization": token, "otherHeader": otherHeader]
let request = Manager.sharedInstance.request(method, URLString, parameters: parameters, encoding: encoding, headers: headers)
If your parameters need to be encoded as JSON, you just need to specify the encoding to .JSON(the default value is .URL).
If your request is supposed to return a JSON you can then trigger it using:
request.responseJSON { (response) -> Void in
// parsing the response here
}
If you need help to parse the response, you can take a look at my alamofire fork, where I implemented a wrapper for the networking framework so that it can be used in Objective-C Alamofire Objective-c wrapper GitHub and this complete walkthrough the changes.
Let me know if you need more information or help with this.

Does Alamofire store the cookies automatically?

I'm new to Alamofire so I'm sorry if this it's a noob question: this framework stores the cookies automatically?
This is because I have a simple request like this:
Alamofire.request(.POST, loginURL, parameters: ["fb_id": fbId, "fb_access_token": fbToken])
.responseJSON { response in
//print(response.request) // original URL request
//print(response.response) // URL response
//print(response.data) // server data
//print(response.result) // result of response serialization
if let JSON = response.result.value {
print("loginURL - JSON: \(JSON)")
}
}
this request response with a cookie session that I need to do other requests for security reason; the strange thing is that like magic I already can do the other requests after this first POST without read manually the cookie and store it. I'm sure the other requests need the cookie session because they fail on postman for example but not here.
It's just a feature? Because I can't find anything on that also on the official GitHub page.
Yes! Alamofire is basically a wrapper around NSURLSession. Its manager uses a default NSURLSessionConfiguration by calling defaultSessionConfiguration().
As its github page says under Advanced Usage section:
Alamofire is built on NSURLSession and the Foundation URL Loading System. To make the most of this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack.
And under Manager section:
Top-level convenience methods like Alamofire.request use a shared instance of Alamofire.Manager, which is configured with the default NSURLSessionConfiguration.
And the NSURLSessionConfiguration reference for defaultSessionConfiguration() says:
The default session configuration uses a persistent disk-based cache (except when the result is downloaded to a file) and stores credentials in the user’s keychain. It also stores cookies (by default) in the same shared cookie store as the NSURLConnection and NSURLDownload classes.
For those who use Moya and want to disable stored cookies
(fixing the X-CSRF-Token request header is missing)
Very basic example:
public final class DisableCookiePlugin: PluginType {
public init() {
}
public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
var mutableRequest = request
mutableRequest.httpShouldHandleCookies = false
return mutableRequest
}
}
And then use it
MoyaProvider<Api>(
plugins: [
//NetworkLoggerPlugin(configuration: .init(logOptions: .verbose)),
DisableCookiePlugin()
]

Send an array of JSON objects in the request body of a POST using the Alamofire request method

I have an array of JSON objects that I would like to pass into the request body of a POST request to our server. However the method signature for the request method only takes a dictionary as its parameters:
func request(method: Alamofire.Method, _ URLString: URLStringConvertible, parameters: [String : AnyObject]? = default, encoding: Alamofire.ParameterEncoding = default, headers: [String : String]? = default) -> Alamofire.Request
Is there a way to currently do this in Alamofire without creating the NSURLRequest yourself? Or is it in the plan for future development?
You will need to use the .Custom parameter encoding to do this or manually create the NSURLRequest yourself. Another possible option would be to modify the server to accept the array with a parameter name in the JSON.
As of right now, there are no plans to support this feature, but thank you for making us aware of this limitation. I have added the issue to our internal Trello board and we will discuss whether we would like to support this in the future.

Trying to set URL and HTTP.body parameters when using Alamofire

I am trying to set the URL parameters here when doing a post to a REST API and for some reason, they are not appearing on the URL when the request is made.
I have seen the other questions posted, but they only set body parameters, I need to supply application key and session id on URL
// return URLRequestConvertible and NSData
var urlParams:[String: String] = Dictionary()
urlParams.updateValue(ACS_API_KEY, forKey: "key")
urlParams.updateValue(SESSION_ID, forKey: "_session_id")
// cannot figure out how to set params on URL
return (Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: urlParams).0, uploadData)
My best guess here is that you are not using a GET, HEAD or DELETE HTTP method in mutableURLRequest. If it is any of the others, then Alamofire will automatically encode the parameters into the HTTP body of the request. For more information, refer to the encode method under the URL case.
As a workaround, you could set the mutableURLRequest to a GET, run the encode method, then apply the URL to a new request. I realize this is a bit of an odd workaround, but it is necessary since your usage case is not the common one that the ParameterEncoding enumeration was designed for.
Hopefully that helps shed some light.

Resources