ios objective-c nsurlsession

ios - Enviar solicitud POST utilizando NSURLSession



objective-c (7)

Actualización: Solución encontrada. Puedes leerlo al final del post.

Estoy intentando realizar una solicitud POST a una API REST remota mediante NSURLSession . La idea es hacer una solicitud con dos parámetros: deviceId y textContent .

El problema es que esos parámetros no son reconocidos por el servidor. La parte del servidor funciona correctamente porque envié un POST utilizando POSTMAN para Google Chrome y funcionó perfectamente.

Este es el código que estoy usando en este momento:

NSString *deviceID = [[NSUserDefaults standardUserDefaults] objectForKey:@"deviceID"]; NSString *textContent = @"New note"; NSString *noteDataString = [NSString stringWithFormat:@"deviceId=%@&textContent=%@", deviceID, textContent]; NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; sessionConfiguration.HTTPAdditionalHeaders = @{ @"api-key" : @"API_KEY", @"Content-Type" : @"application/json" }; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURL *url = [NSURL URLWithString:@"http://url_to_manage_post_requests"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPBody = [noteDataString dataUsingEncoding:NSUTF8StringEncoding]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // The server answers with an error because it doesn''t receive the params }]; [postDataTask resume];

He intentado el mismo procedimiento con una NSURLSessionUploadTask :

// ... NSURL *url = [NSURL URLWithString:@"http://url_to_manage_post_requests"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:[noteDataString dataUsingEncoding:NSUTF8StringEncoding] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // The server answers with an error because it doesn''t receive the params }]; [uploadTask resume];

¿Algunas ideas?

Solución

El problema con mi enfoque fue que estaba enviando el encabezado de Content-Type incorrecto con todas mis solicitudes. Por lo tanto, el único cambio necesario para que el código funcione correctamente es eliminar el encabezado HTTP Content-Type = application/json . Así que el código correcto sería este:

NSString *deviceID = [[NSUserDefaults standardUserDefaults] objectForKey:@"deviceID"]; NSString *textContent = @"New note"; NSString *noteDataString = [NSString stringWithFormat:@"deviceId=%@&textContent=%@", deviceID, textContent]; NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; sessionConfiguration.HTTPAdditionalHeaders = @{ @"api-key" : @"API_KEY" }; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURL *url = [NSURL URLWithString:@"http://url_to_manage_post_requests"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPBody = [noteDataString dataUsingEncoding:NSUTF8StringEncoding]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // The server answers with an error because it doesn''t receive the params }]; [postDataTask resume];

Envío de imágenes junto con otros parámetros.

Si necesita publicar imágenes junto con otros parámetros utilizando NSURLSession aquí tiene un ejemplo:

NSString *deviceID = [[NSUserDefaults standardUserDefaults] objectForKey:@"deviceID"]; NSString *textContent = @"This is a new note"; // Build the request body NSString *boundary = @"SportuondoFormBoundary"; NSMutableData *body = [NSMutableData data]; // Body part for "deviceId" parameter. This is a string. [body appendData:[[NSString stringWithFormat:@"--%@/r/n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=/"%@/"/r/n/r/n", @"deviceId"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"%@/r/n", deviceID] dataUsingEncoding:NSUTF8StringEncoding]]; // Body part for "textContent" parameter. This is a string. [body appendData:[[NSString stringWithFormat:@"--%@/r/n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=/"%@/"/r/n/r/n", @"textContent"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"%@/r/n", textContent] dataUsingEncoding:NSUTF8StringEncoding]]; // Body part for the attachament. This is an image. NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:@"ranking"], 0.6); if (imageData) { [body appendData:[[NSString stringWithFormat:@"--%@/r/n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=/"%@/"; filename=/"image.jpg/"/r/n", @"image"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Type: image/jpeg/r/n/r/n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:imageData]; [body appendData:[[NSString stringWithFormat:@"/r/n"] dataUsingEncoding:NSUTF8StringEncoding]]; } [body appendData:[[NSString stringWithFormat:@"--%@--/r/n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; // Setup the session NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; sessionConfiguration.HTTPAdditionalHeaders = @{ @"api-key" : @"55e76dc4bbae25b066cb", @"Accept" : @"application/json", @"Content-Type" : [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] }; // Create the session // We can use the delegate to track upload progress NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; // Data uploading task. We could use NSURLSessionUploadTask instead of NSURLSessionDataTask if we needed to support uploads in the background NSURL *url = [NSURL URLWithString:@"URL_TO_UPLOAD_TO"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; request.HTTPBody = body; NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // Process the response }]; [uploadTask resume];


Motivación

Algunas veces he estado recibiendo algunos errores cuando se quiere pasar httpBody serializado a Data del Dictionary , que en la mayoría de los casos se debe a una codificación incorrecta o datos mal formados debido a objetos que no NSCoding con la NSCoding en el Dictionary .

Solución

Dependiendo de sus requisitos, una solución fácil sería crear una String lugar de Dictionary y convertirla en Data . Tiene los ejemplos de código a continuación escritos en Objective-C y Swift 3.0 .

C objetivo

// Create the URLSession on the default configuration NSURLSessionConfiguration *defaultSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultSessionConfiguration]; // Setup the request with URL NSURL *url = [NSURL URLWithString:@"yourURL"]; NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; // Convert POST string parameters to data using UTF8 Encoding NSString *postParams = @"api_key=APIKEY&[email protected]&password=password"; NSData *postData = [postParams dataUsingEncoding:NSUTF8StringEncoding]; // Convert POST string parameters to data using UTF8 Encoding [urlRequest setHTTPMethod:@"POST"]; [urlRequest setHTTPBody:postData]; // Create dataTask NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // Handle your response here }]; // Fire the request [dataTask resume];

Rápido

// Create the URLSession on the default configuration let defaultSessionConfiguration = URLSessionConfiguration.default let defaultSession = URLSession(configuration: defaultSessionConfiguration) // Setup the request with URL let url = URL(string: "yourURL") var urlRequest = URLRequest(url: url!) // Note: This is a demo, that''s why I use implicitly unwrapped optional // Convert POST string parameters to data using UTF8 Encoding let postParams = "api_key=APIKEY&[email protected]&password=password" let postData = postParams.data(using: .utf8) // Set the httpMethod and assign httpBody urlRequest.httpMethod = "POST" urlRequest.httpBody = postData // Create dataTask let dataTask = defaultSession.dataTask(with: urlRequest) { (data, response, error) in // Handle your response here } // Fire the request dataTask.resume()


El código de Teja Kumar Bethina cambió para Swift 3:

let urlStr = "http://url_to_manage_post_requests" let url = URL(string: urlStr) var request: URLRequest = URLRequest(url: url!) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField:"Content-Type") request.timeoutInterval = 60.0 //additional headers request.setValue("deviceIDValue", forHTTPHeaderField:"DeviceId") let bodyStr = "string or data to add to body of request" let bodyData = bodyStr.data(using: String.Encoding.utf8, allowLossyConversion: true) request.httpBody = bodyData let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) -> Void in if let httpResponse = response as? HTTPURLResponse { print("responseCode /(httpResponse.statusCode)") } if error != nil { // You can handle error response here print("/(error)") } else { //Converting response to collection formate (array or dictionary) do { let jsonResult = (try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) //success code } catch { //failure code } } } task.resume()


Puede usar https://github.com/mxcl/OMGHTTPURLRQ

id config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:someID]; id session = [NSURLSession sessionWithConfiguration:config delegate:someObject delegateQueue:[NSOperationQueue new]]; OMGMultipartFormData *multipartFormData = [OMGMultipartFormData new]; [multipartFormData addFile:data1 parameterName:@"file1" filename:@"myimage1.png" contentType:@"image/png"]; NSURLRequest *rq = [OMGHTTPURLRQ POST:url:multipartFormData]; id path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"upload.NSData"]; [rq.HTTPBody writeToFile:path atomically:YES]; [[session uploadTaskWithRequest:rq fromFile:[NSURL fileURLWithPath:path]] resume];


Puedes intentar usar un NSDictionary para los params. Lo siguiente enviará los parámetros correctamente a un servidor JSON.

NSError *error; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSURL *url = [NSURL URLWithString:@"[JSON SERVER"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setHTTPMethod:@"POST"]; NSDictionary *mapData = [[NSDictionary alloc] initWithObjectsAndKeys: @"TEST IOS", @"name", @"IOS TYPE", @"typemap", nil]; NSData *postData = [NSJSONSerialization dataWithJSONObject:mapData options:0 error:&error]; [request setHTTPBody:postData]; NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }]; [postDataTask resume];

Espero que esto ayude (estoy tratando de solucionar un problema de autenticidad CSRF con lo anterior, pero envía los parámetros en el NSDictionary).


Si está utilizando Swift, la biblioteca Just hace por usted. Ejemplo de su archivo readme:

// talk to registration end point Just.post( "http://justiceleauge.org/member/register", data: ["username": "barryallen", "password":"ReverseF1ashSucks"], files: ["profile_photo": .URL(fileURLWithPath:"flash.jpeg", nil)] ) { (r) if (r.ok) { /* success! */ } }


La solución Swift 2.0 está aquí:

let urlStr = “http://url_to_manage_post_requests” let url = NSURL(string: urlStr) let request: NSMutableURLRequest = NSMutableURLRequest(URL: url!) request.HTTPMethod = "POST" request.setValue(“application/json” forHTTPHeaderField:”Content-Type”) request.timeoutInterval = 60.0 //additional headers request.setValue(“deviceIDValue”, forHTTPHeaderField:”DeviceId”) let bodyStr = “string or data to add to body of request” let bodyData = bodyStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) request.HTTPBody = bodyData let session = NSURLSession.sharedSession() let task = session.dataTaskWithRequest(request){ (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in if let httpResponse = response as? NSHTTPURLResponse { print("responseCode /(httpResponse.statusCode)") } if error != nil { // You can handle error response here print("/(error)") }else { //Converting response to collection formate (array or dictionary) do{ let jsonResult: AnyObject = (try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) //success code }catch{ //failure code } } } task.resume()


use like this..... Create file #import <Foundation/Foundation.h>` #import "SharedManager.h" #import "Constant.h" #import "UserDetails.h" @interface APISession : NSURLSession<NSURLSessionDelegate> @property (nonatomic, retain) NSMutableData *responseData; +(void)postRequetsWithParam:(NSMutableDictionary* )objDic withAPIName:(NSString* )strAPIURL completionHandler:(void (^)(id result, BOOL status))completionHandler; @end ****************.m************************* #import "APISession.h" #import <UIKit/UIKit.h> @implementation APISession +(void)postRequetsWithParam:(NSMutableDictionary *)objDic withAPIName:(NSString *)strAPIURL completionHandler:(void (^)(id, BOOL))completionHandler { NSURL *url=[NSURL URLWithString:strAPIURL]; NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:url]; [request setHTTPMethod:@"POST"]; [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; NSError *err = nil; NSData *data=[NSJSONSerialization dataWithJSONObject:objDic options:NSJSONWritingPrettyPrinted error:&err]; [request setHTTPBody:data]; NSString *strJsonFormat = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"API URL: %@ /t Api Request Parameter ::::::::::::::%@",url,strJsonFormat); // NSLog(@"Request data===%@",objDic); NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]]; // NSURLSession *session=[NSURLSession sharedSession]; NSURLSessionTask *task=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error==nil) { NSDictionary *dicData=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];/ NSLog(@"Response Data=============%@",dicData); if([[dicData valueForKey:@"tokenExpired"]integerValue] == 1) { NSLog(@"hello"); NSDictionary *dict = [NSDictionary dictionaryWithObject:@"Access Token Expire." forKey:@"message"]; [[NSNotificationCenter defaultCenter] postNotificationName:@"UserLogOut" object:self userInfo:dict]; } dispatch_async(dispatch_get_main_queue(), ^{ completionHandler(dicData,(error == nil)); }); NSLog(@"%@",dicData); } else{ dispatch_async(dispatch_get_main_queue(), ^{ completionHandler(error.localizedDescription,NO); }); } }]; // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [task resume]; // }); } @end *****************************in .your view controller*********** #import "file" txtEmail.text = [txtEmail.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; { [SVProgressHUD showWithStatus:@"Loading..."]; [SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeGradient]; NSMutableDictionary *objLoginDic=[[NSMutableDictionary alloc] init]; [objLoginDic setValue:txtEmail.text forKey:@"email"]; [objLoginDic setValue:@0 forKey:kDeviceType]; [objLoginDic setValue:txtPassword.text forKey:kPassword]; [objLoginDic setValue:@"376545432" forKey:kDeviceTokan]; [objLoginDic setValue:@"" forKey:kcountryId]; [objLoginDic setValue:@"" forKey:kfbAccessToken]; [objLoginDic setValue:@0 forKey:kloginType]; [APISession postRequetsWithParam:objLoginDic withAPIName:KLOGIN_URL completionHandler:^(id result, BOOL status) { [SVProgressHUD dismiss]; NSInteger statusResponse=[[result valueForKey:kStatus] integerValue]; NSString *strMessage=[result valueForKey:KMessage]; if (status) { if (statusResponse == 1) { UserDetails *objLoginUserDetail=[[UserDetails alloc] initWithObject:[result valueForKey:@"userDetails"]]; [[NSUserDefaults standardUserDefaults] setObject:@(objLoginUserDetail.userId) forKey:@"user_id"]; [[NSUserDefaults standardUserDefaults] synchronize]; [self clearTextfeilds]; HomeScreen *obj=[Kiran_Storyboard instantiateViewControllerWithIdentifier:@"HomeScreen"]; [self.navigationController pushViewController:obj animated:YES]; } else{ [strMessage showAsAlert:self]; } } }]; } **********use model class for represnt data************* #import <Foundation/Foundation.h> #import "Constant.h" #import <objc/runtime.h> @interface UserDetails : NSObject @property(strong,nonatomic) NSString *emailId, *deviceToken, *countryId, *fbAccessToken, *accessToken, *countryName, *isProfileSetup, *profilePic, *firstName, *lastName, *password; @property (assign) NSInteger userId,deviceType,loginType; -(id)initWithObject :(NSDictionary *)dicUserData; -(void)saveLoginUserDetail; +(UserDetails *)getLoginUserDetail; -(UserDetails *)getEmptyModel; - (NSArray *)allPropertyNames; -(void)printDescription; -(NSMutableDictionary *)getDictionary; @end ******************model.m************* #import "UserDetails.h" #import "SharedManager.h" @implementation UserDetails -(id)initWithObject :(NSDictionary *)dicUserData { self = [[UserDetails alloc] init]; if (self) { @try { [self setFirstName:([dicUserData valueForKey:@"firstName"] != [NSNull null])? [dicUserData valueForKey:@"firstName"]:@""]; [self setUserId:([dicUserData valueForKey:kUserId] != [NSNull null])? [[dicUserData valueForKey:kUserId] integerValue]:0]; } @catch (NSException *exception) { NSLog(@"Exception: %@",exception.description); } @finally { } } return self; } -(UserDetails *)getEmptyModel{ [self setFirstName:@""]; [self setLastName:@""]; [self setDeviceType:0]; return self; } - (void)encodeWithCoder:(NSCoder *)encoder { // Encode properties, other class variables, etc [encoder encodeObject:_firstName forKey:kFirstName]; [encoder encodeObject:[NSNumber numberWithInteger:_deviceType] forKey:kDeviceType]; } - (id)initWithCoder:(NSCoder *)decoder { if((self = [super init])) { _firstName = [decoder decodeObjectForKey:kEmailId]; _deviceType= [[decoder decodeObjectForKey:kDeviceType] integerValue]; } return self; } - (NSArray *)allPropertyNames { unsigned count; objc_property_t *properties = class_copyPropertyList([self class], &count); NSMutableArray *rv = [NSMutableArray array]; unsigned i; for (i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *name = [NSString stringWithUTF8String:property_getName(property)]; [rv addObject:name]; } free(properties); return rv; } -(void)printDescription{ NSMutableDictionary *dic = [[NSMutableDictionary alloc] init]; for(NSString *key in [self allPropertyNames]) { [dic setValue:[self valueForKey:key] forKey:key]; } NSLog(@"/n========================= User Detail ==============================/n"); NSLog(@"%@",[dic description]); NSLog(@"/n=============================================================/n"); } -(NSMutableDictionary *)getDictionary{ NSMutableDictionary *dic = [[NSMutableDictionary alloc] init]; for(NSString *key in [self allPropertyNames]) { [dic setValue:[self valueForKey:key] forKey:key]; } return dic; } #pragma mark #pragma mark - Save and get User details -(void)saveLoginUserDetail{ NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:self]; [Shared_UserDefault setObject:encodedObject forKey:kUserDefault_SavedUserDetail]; [Shared_UserDefault synchronize]; } +(UserDetails *)getLoginUserDetail{ NSData *encodedObject = [Shared_UserDefault objectForKey:kUserDefault_SavedUserDetail]; UserDetails *object = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject]; return object; } @end ************************usefull code while add data into model and get data******** NSLog(@"Response %@",result); NSString *strMessg = [result objectForKey:kMessage]; NSString *status = [NSString stringWithFormat:@"%@",[result objectForKey:kStatus]]; if([status isEqualToString:@"1"]) { arryBankList =[[NSMutableArray alloc]init]; NSMutableArray *arrEvents=[result valueForKey:kbankList]; ShareOBJ.isDefaultBank = [result valueForKey:kisDefaultBank]; if ([arrEvents count]>0) { for (NSMutableArray *dic in arrEvents) { BankList *objBankListDetail =[[BankList alloc]initWithObject:[dic mutableCopy]]; [arryBankList addObject:objBankListDetail]; } //display data using model... BankList *objBankListing =[arryBankList objectAtIndex:indexPath.row];