AFNetworking v3.1.0 multipartFormRequestWithMethod上传带引号的JSON数值

当我使用AFNetworking v3.1.0 multipartFormRequestWithMethod上传文件并包含一个参数字典,该参数字典被转换为JSON,并且字典中有一个条目如:

 ["Test": 1] 

也就是说,一个带有数值的键,当它到达我的服务器时,JSON结构是一个带引号的string。 例如:

 {"Test":"1"} 

不是我想要的。 我想在服务器上收到的是:

 {"Test": 1} 

请注意,当使用AFHTTPSessionManager的POST方法(我认为不允许file upload)时,我不会遇到这个问题。

这是AFNetworking中的一个错误吗? 我在做什么? 有没有解决方法或其他解决scheme? 我已经在AFNetworking JSON序列化问题上看到了这个讨论,但是我的应用程序并不适合在服务器上进行parsing,以便按照我喜欢的方式(数字不带引号)进行parsing。 思考?

这是我使用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() } } 

我已经使用了一个无参数的值来constructingBodyWithBlock BodyWithBlock,因为当我上传文件的时候,当我没有(这个例子实际上没有上传文件)出现问题。

以下是调用这些方法的代码:

 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)") }*/ } } 

为了完整起见,我将包含用于testing的Node.js服务器:

 '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')); }); 

这里是服务器的package.json:

 { "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" } } 

事实certificate,这个问题可能是最好的不认为AFNetworking错误。

这是我在客户端做的改变:

 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)") }*/ } 

以下是我对服务器所做的更改:

 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); } 

另见https://github.com/AFNetworking/AFNetworking/issues/3544