Отправить запрос POST с использованием NSURLSession
Обновление: Решение найдено. Вы можете прочитать его в конце сообщения.
Я пытаюсь выполнить запрос POST для удаленного REST API с помощью NSURLSession
. Идея состоит в том, чтобы сделать запрос с двумя параметрами: deviceId
и textContent
.
Проблема заключается в том, что эти параметры не распознаются сервером. Часть сервера работает правильно, потому что я отправил POST с помощью POSTMAN для Google Chrome, и он отлично работал.
Это код, который я использую прямо сейчас:
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];
Я пробовал ту же процедуру с 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];
Любые идеи?
Решение
Проблема с моим подходом заключалась в том, что я отправлял неправильный заголовок Content-Type
со всеми моими запросами. Поэтому единственное изменение, необходимое для правильной работы кода, - удалить заголовок Content-Type = application/json
HTTP. Таким образом, правильный код будет следующим:
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];
Отправка изображений вместе с другими параметрами
Если вам нужно отправить изображения вместе с другими параметрами, используя NSURLSession
здесь, у вас есть пример:
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];
Ответы
Ответ 1
Вы можете попробовать использовать NSDictionary для параметров. Следующие будут правильно отправлять параметры на сервер 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];
Надеюсь, что это поможет (я пытаюсь разобраться с проблемой аутентификации CSRF с указанным выше, но он отправляет параметры в NSDictionary).
Ответ 2
Мотивация
Иногда я получаю некоторые ошибки, когда вы хотите передать httpBody сериализованному в Data
из Dictionary
, что в большинстве случаев связано с неправильной кодировкой или искаженными данными из-за не NSCoding соответствующие объекты в Dictionary
.
Решение
В зависимости от ваших требований одним простым решением было бы создать String
вместо Dictionary
и преобразовать его в Data
. У вас есть примеры кода ниже, написанные на Objective-C
и Swift 3.0
.
Objective-C
// 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];
Swift
// 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 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()
Ответ 3
Вы можете использовать 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];
Ответ 4
Если вы используете Swift, библиотека Just сделает это за вас. Пример из файла 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! */ }
}
Ответ 5
Решение Swift 2.0 находится здесь:
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()
Ответ 6
Код Teja Kumar Bethina изменился для 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()