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