Http Header Authentication on post request AFNetworking - objective-c

I am trying to create a multipartFormRequestWithMethod using AFNetworking's AFHttpClient. It must authenticate with a ASP.NET REST service using Http Authorization Header. Therefore I pass username and password to the Http Authorization Header using setAuthorizationHeaderWithUsername:password:. In the body of the request I am passing a large file, several MBs. If the Header authentication fails, I want the request to get block and go in failure state before finishing the file send. There should be something that prevents the database send in case of authentication failure, but I cannot figure out what. In the current situation the AFHttpRequestOperation starts to send the file and notifies of the error only at the end of the file send.
This is the code:
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://v-moxdevelop/MOX.UploadDBService/UploadDB/"]];
NSString *psw = #"psw";
NSString *userName = #"username";
[httpClient setAuthorizationHeaderWithUsername:selectedLoginItem.user password:#"psw"];
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path: [NSString stringWithFormat:#"Upload/%#/%#/%#", UDID, [selectedLoginItem.firm stringByReplacingOccurrencesOfString:#"." withString:#""], [selectedLoginItem.user stringByReplacingOccurrencesOfString:#"." withString:#""]] parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:fileData name:zipFilePath fileName:[zipFilePath lastPathComponent] mimeType:#"application/zip"];
}];
[request setTimeoutInterval:INT32_MAX];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
__weak typeof(self) weakSelf = self;
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *locOperation, id responseObject) {
}failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
}];
[httpClient enqueueHTTPRequestOperation:operation];

Related

Having problems using AFNetworking fetching JSON

So i recently wanted to change from using NSUrlConnection to AFNetworking. I can receive the JSON data with both methods buts when using with AFNetworking something weird happens.
This is how it looks like with NSURLConnection
and this is how it looks like with AFNetworking
I have no idea what that (struct __lidb_autoregen_nspair) is and i dont know if that is the thing that is preventing me from displaying the data
This is the code from AFNetworking, i use the sample code from ray
-(void) fetchData{
// 1
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 2
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// 3
jsonDict = (NSMutableDictionary *)responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// 4
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}];
// 5
[operation start];
}
----------------------------------------------------------------------------------------- Edit
-(NSMutableDictionary *) getAllGames{
[self fetchData];
DataParser *dataParserObjec = [[DataParser alloc] init];
return [dataParserObjec sendBackAllGames:jsonDict];
}
You are setting the acceptableContentTypes to text/html. I presume you are doing that because your web-service is not setting the correct Content-Type header to indicate that it's application/json. If you fixed the web service to provide the correct header Content-Type, you could then remove this acceptableContentTypes line in your Objective-C code.
If you're wondering why you didn't have to worry about that with NSURLConnection, that's because NSURLConnection doesn't do any validation of Content-Type (unless, of course, you write your own code in, for example, didReceiveResponse, that checked this).
You suggest that you are unable to display the data. But yet there it is, in your second screen snapshot. I personally would be less worried about internal representation than whether I could access the data from the NSDictionary. If you
NSLog(#"responseObject=%#", responseObject);
at #3, inside the success block, what precisely do you see? I'd wager you'll see your NSDictionary fine (despite the subtle differences in the internal representation).
My contention is that you are getting the data back successfully. Yes, your web service should set the correct Content-Type header so you don't have to overwrite the acceptableContentTypes value, but it looks like AFNetworking is retrieving your data fine.
The likely issue is that your main thread is trying to use jsonDict before the asynchronous network request is done. So the trick is to defer the use of the jsonDict until the asynchronous request is done.
You've updated your question showing us that you're instantiating a DataParser and calling sendBackAllGames. You should put that code inside the completion block of your asynchronous network request.
Alternatively, you could use a completion block pattern in your fetchData method, and then the getAllGames method could supply the sendBackAllGames code in a completion block that fetchData calls inside the success block of the AFHTTPRequestOperation.
If you used the completion block pattern, it would look like
-(void) fetchDataWithCompletionHandler:(void (^)(id responseObject, NSError *error))completionHandler {
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
// removed once we fixed the `Content-Type` header on server
//
// operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
[operation start];
}
And you'd call it like so:
[self fetchDataWithCompletionHandler:^(id responseObject, NSError *error) {
if (responseObject) {
DataParser *dataParserObjec = [[DataParser alloc] init];
NSMutableDictionary *results = [dataParserObjec sendBackAllGames:jsonDict];
// do whatever you want with results
}
}];
If the method that called getAllGames needed the data to be returned, you'd repeat this completion block pattern for this method, too.

Upload PowerPoint via AFNetworking gets corrupt

I receive a successfully response when uploading a PowerPoint via the below code. It does get upload but the file is corrupt. When opening the corrupt file on the server via PowerPoint I get this message:
"PowerPoint found a problem with content in filename.pptx. PowerPoint can attempt to repair the presentation."
- (void)updateDocument:(NSString *) path parameters:(FileUploadParameters*)para success:(void (^)(void))success failure:(void (^)(NSError *error))failure
{
_postData = nil;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[ConfigurationUtil objectForKey:#"baseURL"]]];
_postData = [NSMutableData dataWithContentsOfFile:[path stringByStandardizingPath]];
_postData = [NSMutableData dataWithContentsOfFile:[path stringByStandardizingPath] options:NSDataReadingMapped error:nil];
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:HTTP_METHOD_POST
path:[self getQueryString:path parameter:para]
parameters:nil
constructingBodyWithBlock: ^(id <AFMultipartFormData>formData)
{
[formData appendPartWithFileData:_postData
name:[[path lastPathComponent] stringByDeletingPathExtension]
fileName:[[path lastPathComponent] stringByDeletingPathExtension] mimeType:#"application/powerpoint"];
}];
[request addValue:[NSString stringWithFormat:#"WRAP access_token=%#",[Tenant loadSharedTenantInstance].authToken] forHTTPHeaderField:#"Authorization"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
if (success)
success();
}
failure:^(AFHTTPRequestOperation* operation, NSError* error)
{
if (failure)
failure(error);
}
];
[operation start];
}
Your AFNetworking code looks fine. I don't think that's the culprit.
You can use the cmp command line tool to compare the two files (the original and the upload) byte-for-byte and see if there's a difference. I think there won't be.
More likely: it's a misleading error message, and it's actually a permissions issue on the computer with Powerpoint installed, as outlined in this Microsoft Knowledge Base article.
The below code corrects the problem. It appears that appendPartWithFileData was causing the issue. It changes the filesize and that seemed to register the file as corrupt.
_postData = nil;
_postData = [NSMutableData dataWithContentsOfFile:[path stringByStandardizingPath]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
NSString *queryString =[self getQueryString:path parameter:nil];
[request setURL:[NSURL URLWithString:queryString]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:_postData];
[request addValue:[NSString stringWithFormat:#"WRAP access_token=%#",[Tenant loadSharedTenantInstance].authToken] forHTTPHeaderField:#"Authorization"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
if (success)
success();
}
failure:^(AFHTTPRequestOperation* operation, NSError* error)
{
if (failure)
failure(error);
}
];
[operation start];

AFNetworking 2.0 and HTTP Basic Authentication

Can't find AFHTTPClient on AFNetworking 2.0, to use:
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://examplewebsite.com]];
[client setAuthorizationHeaderWithUsername:#"username" password:#"password"];
How it needs to be manage on AFNetworking 2.0?
AFNetworking 2.0 new architecture use serializers for creating requests and parsing responses.
In order to set the authorization header, you should first initialize a request operation manager that replaces the AFHTTPClient, create a serializer and then call the dedicated method to set the header.
For example you code would become:
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:#"http://examplewebsite.com"]];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"userName" password:#"password"];
You should read the documentation and the migration guide to understand the new concepts that come with the version 2.0 of AFNetworking.
Here is an example of performing basic HTTP authentication with AFNetworking 2.0 using NSURLCredential. The advantage of this approach over using the AFHTTPRequestSerializer setAuthorizationHeaderFieldWithUsername:password: method is that you can automatically store the username and password in the keychain by changing the persistence: parameter of NSURLCredential. (See this answer.)
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"user" password:#"passwd" persistence:NSURLCredentialPersistenceNone];
NSMutableURLRequest *request = [manager.requestSerializer requestWithMethod:#"GET" URLString:#"https://httpbin.org/basic-auth/user/passwd" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCredential:credential];
[operation setResponseSerializer:[AFJSONResponseSerializer alloc]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
[manager.operationQueue addOperation:operation];
As #gimenete mentions multipart requests will fail when using #titaniumdecoy credential approach as this is applied in the challenge block and the current version of AFNetworking has an issue with this. Instead of using the credential approach you can embed the authentication in the NSMutableRequest header
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"PUT" URLString:path parameters:myParams constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:imageData name:imageName fileName:imageName mimeType:#"image/jpeg"];
} error:&error];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", [self username], [self password]];
NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *authValue = [NSString stringWithFormat:#"Basic %#", [authData base64EncodedString]];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
Where you will need to use a third party BASE64 encoding library such as NSData+Base64.h and .m File from Matt Gallaghers pre ARC BASE64 solution

objective-c post data & image

I am new to objective-c and I have been searching for a way to send a post request to my server (based on Rest URL) but also include an image with it... I have found many methods to post data... and methods to post just an image, but nothing that combines the two...
I am searching for a wrapper, class or library because it seems to be a tedious task to write all this from scratch. I found the "ASIHTTPRequest" but this is no longer supported, although Ic an turn off ARC, I would prefer to find something still supported...
I also found AFNetworking, which seems to still be supported but I could be wrong, I just cannot find a solution to combine VERY simply data and a profile image...
Any help is appreciated?
Should I just use the ASIHTTPRequest library... ?? Or does anyone have any sample code for the AFNetworking library?
Here is the code I am using for AFnetworking library...
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
_emailAddressField.text, #"email",
_usernameField.text, #"username",
_passwordField.text, #"password",
nil];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:%"http://url.com/api/whatever/"];
[client postPath:#"/" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *text = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#", text);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%#", [error localizedDescription]);
}];
If you're using AFNetworking, you can use multipartFormRequestWithMethod to upload an image:
// Create the http client
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseUrl:url];
// Set parameters
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys: #"param1", #"key1", nil];
// Create the request with the image data and file name, mime type, etc.
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:method path:#"url/to/" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:nameData fileName:fileName mimeType:mimeType];
}];
And then you can add the upload progress block to get feedback of the upload process:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
//Manage upload percentage
}];
Also, you can add setCompletionBlockWithSuccess to catch success and failure in your operation. More info can be found here. At last but not least important, add the request to the operation queue:
[httpClient enqueueHTTPRequestOperation:operation];

Pass response from AFHTTPRequestOperation to UIWebview

My code is making two requests to the server. One into the uiwebview directly and one with the AFHTTPRequestOperation. I'd like to use the AFHTTPRequestOperation and just pass the response into my uiwebview. What is the correct syntax for passing the response into the uiwebview and not loading it again? How do I do that without calling twice from the server? I still want to be able to test the success or failure of the load request and also send username and password to connect to the url.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[itemField resignFirstResponder];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *userName = [defaults objectForKey:#"storedUserName"];
NSString *passWord = [defaults objectForKey:#"storedPassWord"];
NSURL *url = [NSURL URLWithString:#"http://www.example.net/"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL: url];
[client setAuthorizationHeaderWithUsername:userName password:passWord];
NSMutableURLRequest *request = [client requestWithMethod:#"GET" path:[#"/example/itemlookup.php?item=" stringByAppendingString:itemField.text] parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//calling again here was the only way I could figure out to get this into my webview
//need to get response from above and put into the uiwebview
[webView loadRequest:request];
NSLog(#"Success");
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
return YES;
}
I'd recommend using AFNetworking's UIWebView category which does exactly this; uses an AFHTTPRequestOperation to load the content of a webpage and then loads it into the web view as an HTML string.
Otherwise I'd recommend taking a look at the category to see if you can adapt its code for your use. https://github.com/AFNetworking/AFNetworking/blob/master/UIKit%2BAFNetworking/UIWebView%2BAFNetworking.m
If that URL is returning HTML, you can use the - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL method of the UIWebView.
Something like:
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[webView loadHTMLString:responseObject baseURL:url]; //If responseObject is HTML
NSLog(#"Success");
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
The only thing that might change is the response object. You may be not getting a string, but a NSURLResponse or something to that nature.