2016-05-25 1 views
0

Lorsque je télécharger un fichier en utilisant AFNetworking v3.1.0 multipartFormRequestWithMethod et inclure un dictionnaire de paramètre, qui est converti en JSON, et le dictionnaire a une entrée telle que:AFNetworking v3.1.0 multipartFormRequestWithMethod Envois JSON valeurs numériques avec des citations

["Test": 1] 

soit une clé avec une valeur numérique, la structure JSON lorsqu'elle arrive sur mon serveur est une chaîne entre guillemets. Par exemple:

{"Test":"1"} 

C'est pas ce que je veux. Ce que je veux reçu sur le serveur est:

{"Test": 1} 

Notez que je ne pas obtenir ce problème lors de l'utilisation de la méthode POST de AFHTTPSessionManager (que je crois ne permet pas le téléchargement de fichiers).

Est-ce un bug dans AFNetworking? Est-ce que je fais quelque chose? Y a-t-il une solution de contournement ou une autre solution? J'ai vu la discussion au AFNetworking JSON serialization problems mais mon application n'est pas vraiment adaptée à l'analyse du serveur pour la ramener à la façon dont je l'aimerais (les numéros sans les guillemets). Pensées?

Voici mon code client qui utilise AFNetworking:

import Foundation 
import SMCoreLib // some common library functions I use 

class Server { 
    private let manager: AFHTTPSessionManager! 
    internal static let session = Server() 
    var uploadTask:NSURLSessionUploadTask? 

    private init() { 
     self.manager = AFHTTPSessionManager() 
      // https://stackoverflow.com/questions/26604911/afnetworking-2-0-parameter-encoding 
     self.manager.responseSerializer = AFJSONResponseSerializer() 

     // This does appear necessary for requests going out to server to receive properly encoded JSON parameters on the server. 
     self.manager.requestSerializer = AFJSONRequestSerializer() 

     self.manager.requestSerializer.setValue("application/json", forHTTPHeaderField: "Content-Type") 
    } 

    // In the completion hanlder, if error != nil, there will be a non-nil serverResponse. 
    internal func sendServerRequestTo(toURL serverURL: NSURL, withParameters parameters:[String:AnyObject], 
     completion:((serverResponse:[String:AnyObject]?, error:NSError?)->())?) { 

     Log.special("serverURL: \(serverURL)") 

     if !Network.connected() { 
      Log.msg("Network not connected!") 
      completion?(serverResponse: nil, error: Error.Create("Network not connected!")) 
      return 
     } 

     self.manager.POST(serverURL.absoluteString, parameters: parameters, progress: nil, 
      success: { (request:NSURLSessionDataTask, response:AnyObject?) in 
       if let responseDict = response as? [String:AnyObject] { 
        Log.msg("AFNetworking Success: \(response)") 
        completion?(serverResponse: responseDict, error: nil) 
       } 
       else { 
        completion?(serverResponse: nil, error: Error.Create("No dictionary given in response")) 
       } 
      }, 
      failure: { (request:NSURLSessionDataTask?, error:NSError) in 
       print("**** AFNetworking FAILURE: \(error)") 
       completion?(serverResponse: nil, error: error) 
      }) 
    } 

    // withParameters must have a non-nil key SMServerConstants.fileMIMEtypeKey 
    internal func uploadFileTo(serverURL: NSURL, withParameters parameters:[String:AnyObject]?, completion:((serverResponse:[String:AnyObject]?, error:NSError?)->())?) { 

     Log.special("serverURL: \(serverURL)") 

     if !Network.connected() { 
      completion?(serverResponse: nil, error: Error.Create("Network not connected.")) 
      return 
     } 

     var error:NSError? = nil 

     let request = AFJSONRequestSerializer().multipartFormRequestWithMethod("POST", URLString: serverURL.absoluteString, parameters: parameters, constructingBodyWithBlock: nil, error: &error) 

     if nil != error { 
      completion?(serverResponse: nil, error: error) 
      return 
     } 

     self.uploadTask = self.manager.uploadTaskWithStreamedRequest(request, progress: { (progress:NSProgress) in 
      }, 
      completionHandler: { (request: NSURLResponse, responseObject: AnyObject?, error: NSError?) in 
       if (error == nil) { 
        if let responseDict = responseObject as? [String:AnyObject] { 
         Log.msg("AFNetworking Success: \(responseObject)") 
         completion?(serverResponse: responseDict, error: nil) 
        } 
        else { 
         let error = Error.Create("No dictionary given in response") 
         Log.error("**** AFNetworking FAILURE: \(error)") 
         completion?(serverResponse: nil, error: error) 
        } 
       } 
       else { 
        Log.error("**** AFNetworking FAILURE: \(error)") 
        completion?(serverResponse: nil, error: error) 
       } 
      }) 

     if nil == self.uploadTask { 
      completion?(serverResponse: nil, error: Error.Create("Could not start upload task")) 
      return 
     } 

     self.uploadTask?.resume() 
    } 
} 

J'ai utilisé une valeur de paramètre zéro pour constructingBodyWithBlock parce que la question se pose quand je télécharge un fichier et quand je ne suis pas (cet exemple ne pas télécharger un fichier).

Voici le code qui appelle ces méthodes:

import UIKit 
import SMCoreLib 

class ViewController: UIViewController { 

    //let serverURL = NSURL(string: "http://192.168.3.228:8082/json/")! 
    let serverURL = NSURL(string: "http://192.168.3.228:8082/upload/")! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 

     let params = ["Test" : 1] 

     Server.session.uploadFileTo(serverURL, withParameters:params){ (serverResponse, error) in 
      Log.msg("serverResponse: \(serverResponse); error: \(error)") 
     } 

     /* 
     Server.session.sendServerRequestTo(toURL: self.serverURL, withParameters: params) { (serverResponse, error) in 
      Log.msg("serverResponse: \(serverResponse); error: \(error)") 
     }*/ 
    } 
} 

Pour être complet, je rajouterais le serveur Node.js J'utilise pour les tests:

'use strict'; 

var express = require('express'); 
var bodyParser = require('body-parser'); 
var app = express(); 
var multer = require('multer'); 

// https://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js 
app.use(bodyParser.json({extended : true})); 

const fileUploadFieldName = "uploadFile"; 
const initialUploadDirectory = "./uploadedFiles"; 

// See https://stackoverflow.com/questions/31496100/cannot-app-usemulter-requires-middleware-function-error 
// See also https://codeforgeek.com/2014/11/file-uploads-using-node-js/ 
// TODO: Limit the size of the uploaded file. 
// TODO: Is there a way with multer to add a callback that gets called periodically as an upload is occurring? We could use this to "refresh" an activity state for a lock to make sure that, even with a long-running upload (or download) if it is still making progress, that we wouldn't lose a lock. 
var upload = multer({ dest: initialUploadDirectory}).single(fileUploadFieldName); 

function handleBody(request, response) { 
    response.setHeader('Content-Type', 'application/json'); 

    console.log("Got request!"); 
    const json = request.body["Test"]; 
    console.log("json: " + json); 

    const fullBody = JSON.stringify(request.body); 
    console.log("fullBody: " + fullBody); 
    response.end(fullBody); 
} 

app.post("/json" , function(request, response) { 
    handleBody(request, response); 
}); 

app.post("/upload", upload, function (request, response) { 
    handleBody(request, response); 
}); 

app.set('port', 8082); 
app.listen(app.get('port'), function() { 
    console.log('Node app is running on port', app.get('port')); 
}); 

Et voici le paquet. json pour le serveur:

{ 
    "name": "jsonserver", 
    "version": "1.0.0", 
    "description": "", 
    "main": "index.js", 
    "scripts": { 
    "test": "echo \"Error: no test specified\" && exit 1" 
    }, 
    "author": "", 
    "license": "ISC", 
    "dependencies": { 
    "body-parser": "^1.15.1", 
    "express": "^4.13.4", 
    "multer": "^1.1.0" 
    } 
} 

Répondre

0

Il s'avère que ce problème n'est probablement pas considéré comme une erreur AFNetworking.

Voici le changement que je fait sur le client:

override func viewDidLoad() { 
    super.viewDidLoad() 
    // Do any additional setup after loading the view, typically from a nib. 

    // Params you actually want received at the server. 
    let params = ["Test" : 1, "Test2": 2] 

    var jsonData:NSData? 

    do { 
     try jsonData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions(rawValue: 0)) 
    } catch (let error) { 
     Assert.badMojo(alwaysPrintThisString: "Yikes: Error serializing to JSON data: \(error)") 
    } 

    // The server needs to pull "serverParams" out of the request body, then convert the value to JSON 
    let serverParams = ["serverParams" : jsonData!] 

    Server.session.uploadFileTo(serverURL, withParameters: serverParams){ (serverResponse, error) in 
     Log.msg("serverResponse: \(serverResponse); error: \(error)") 
    } 

    /* 
    Server.session.sendServerRequestTo(toURL: self.serverURL, withParameters: params) { (serverResponse, error) in 
     Log.msg("serverResponse: \(serverResponse); error: \(error)") 
    }*/ 
} 

Voici le changement que je fait au serveur:

function handleBody(request, response) { 
    response.setHeader('Content-Type', 'application/json'); 

    console.log("Got request!"); 
    const jsonObject = JSON.parse(request.body["serverParams"]); 
    const jsonString = JSON.stringify(jsonObject); 
    console.log("json: " + jsonString); 

    response.end(jsonString); 
} 

Voir aussi https://github.com/AFNetworking/AFNetworking/issues/3544