2014-10-18 10 views
5

Sto creando un endpoint di callback per l'attivazione di un'API di terze parti. Il chiamante invierà ad esso i dati del modulo multiparte.Serializza i nomi di proprietà alternative dal modulo in POST

Qualcosa di simile a questo:

public SomeApiController : ApiController { 
    public AModel Post(AModel input) { 
    return input; //for demonstration 
    } 
} 

Alcuni dei campi sarà Post hanno trattini nel nome, che non può essere un nome effettivo di proprietà .NET. Quindi, ho usato [DataContract] e [DataMember (name = "blah")] per definire le regole di serializzazione.

Modello:

//input model class 
[DataContract] 
public class AModel { 
    [DataMember] 
    public string NormalProperty {get; set;} //value set appropriately 
    [DataMember(Name="abnormal-property")] 
    public string AbnormalProperty {get; set;} //always null (not serializing) 
} 

Con i messaggi XML e JSON standard, questo funziona bene. Le normali e AbnormalProperty sono impostate e posso portare avanti la mia attività.

Tuttavia, con qualsiasi variante di dati del modulo (form-data, multiplart/form-data,-form-urlencoded dati x-, AbnormalProperty non correttamente deserializzare nel modello, e sarà sempre nullo.

c'è una direttiva mi manca o cosa?

risposta

0

Dopo un sacco di colpi di testa, abbiamo più o meno fatto una combinazione di due cose.

Per i moduli con codifica url, abbiamo seguito l'esempio di The Zen Coder e funziona in modo impeccabile.

Tuttavia, per i moduli multiparte tradizionali, che sono trasmessi in streaming al server (possono contenere dati di file), abbiamo creato una soluzione semplice per leggere la chiave/i valori dalla richiesta e quindi serializzati manualmente.

Nel nostro caso d'uso particolare, non dobbiamo preoccuparci di dati di file in arrivo o di qualcosa, quindi possiamo solo presumere che tutto è una stringa e fare lavoro di serializzazione da lì. Tuttavia, potrebbe essere saggio aggiungere un semplice controllo dell'intestazione ContentDisposition per i file reali.

Esempio:

using System; 
using System.Collections.Generic; 
using System.Collections.Specialized; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Net.Http; 
using System.Threading.Tasks; 
using System.Web.Http; 

namespace WebApplication1.Controllers 
{ 
    public class ValuesController : ApiController 
    { 
     public async Task<Dictionary<string,string>> Post() 
     { 
      if (!Request.Content.IsMimeMultipartContent()) 
       throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 
      var provider = new MultipartMemoryStreamProvider(); 
      var formData = new Dictionary<string,string>(); 
      try 
      { 
       await Request.Content.ReadAsMultipartAsync(provider); 
       foreach (var item in provider.Contents) 
       { 
        formData.Add(item.Headers.ContentDisposition.Name.Replace("\"",""), await item.ReadAsStringAsync()); 
       } 

       return formData; 
      } 
      catch (Exception e) 
      { 
       throw new HttpResponseException(HttpStatusCode.InternalServerError); 
      } 
     }  
    } 
} 
4

I HAV Ho provato il tuo esempio e la mia conclusione è che lo DefaultModelBinder in ASP.NET MVC non supporta la denominazione delle variabili POST.

Una soluzione ovvia non è usare il trattino nei vostri nomi.

Se questa non è un'opzione, è possibile implementare il proprio modello di raccoglitore per quel modello specifico per gestire i nomi anomali che si inviano al controller MVC. Ecco un esempio di un modello personalizzato legante:

public class AModelDataBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType == typeof(AModel)) 
     { 
      var request = controllerContext.HttpContext.Request; 

      string normal = request.Form.Get("normalproperty"); 
      string abnormal = request.Form.Get("abnormal-property"); 

      return new AModel 
      { 
       NormalProperty = normal, 
       AbnormalProperty = abnormal 
      }; 
     } 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

allora si dovrebbe registrare questo legante personalizzato in Global.asax:

ModelBinders.Binders.Add(typeof(AModel), new AModelDataBinder()); 

Ed infine è possibile utilizzare il legante personalizzato nel controller:

[HttpPost] 
public ActionResult Index([ModelBinder(typeof(AModelDataBinder))]AModel input) 
{ 
    // Handle POST 

    return View(); 
} 
Problemi correlati