Come passare DataTable tramite FromBody al metodo POST API Web (C #)

Sto chiamando con successo un metodo POST in un’app di API Web da un client Winforms che passa alcuni parametri per una stored procedure.

Preferirei, tuttavia, passare i risultati della stored procedure (che devo eseguire prima sul client) al metodo POST tramite la funzionalità FromBody, se ansible.

Sono molti i dati da inviare via cavo, ma nel modo in cui lo sto facendo ora devo eseguire l’SP due volte – prima sull’app Winforms del client, poi sull’app del server API Web e la chiamata simultanea di questo SP sembra a volte causare alcuni problemi.

Quindi, mi piacerebbe, se ansible, inviare il DataTable via “FromBody” o, se preferibile, una versione XMLized o jsonized dei dati (e quindi decomprimerlo dall’altra parte, dove lo converto in html per il recupero quando viene chiamato il metodo GET corrispondente.

Qualcuno ha un codice che fa ciò che possono mostrare?

Il mio codice esistente che passa semplicemente i parametri può essere visto qui .

AGGIORNARE

Ok, sulla base della risposta di Amit Kumar Ghosh, ho cambiato il mio codice in questo modo:

WebApiConfig.cs

public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Configure Web API to use only bearer token authentication. config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Formatters.Add(new DataTableMediaTypeFormatter()); } public class DataTableMediaTypeFormatter : BufferedMediaTypeFormatter { public DataTableMediaTypeFormatter() : base() { SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("test/dt")); } public override object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, System.Threading.CancellationToken cancellationToken) { var data = new StreamReader(readStream).ReadToEnd(); var obj = JsonConvert.DeserializeObject(data); return obj; } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } } 

CONTROLLER

 [Route("{unit}/{begindate}/{enddate}/{stringifiedjsondata}")] [HttpPost] public void Post(string unit, string begindate, string enddate, DataTable stringifiedjsondata) { DataTable dt = stringifiedjsondata; . . . 

CLIENTE

 private async Task SaveProduceUsageFileOnServer(string beginMonth, string beginYear, string endMonth, string endYear) { string beginRange = String.Format("{0}{1}", beginYear, beginMonth); string endRange = String.Format("{0}{1}", endYear, endMonth); HttpClient client = new HttpClient(); client.BaseAddress = new Uri("http://localhost:52194"); string dataAsJson = JsonConvert.SerializeObject(_rawAndCalcdDataAmalgamatedList, Formatting.Indented); String uriToCall = String.Format("/api/produceusage/{0}/{1}/{2}/{3}", _unit, beginRange, endRange, @dataAsJson); HttpResponseMessage response = await client.PostAsync(uriToCall, null); } 

… ma il controller non è ancora stato raggiunto; in particolare, il punto di interruzione in “DataTable dt = dtPassedAsJson;” non è mai raggiunto.

In realtà, mi sorprende che non si blocchi, poiché viene passata una stringa, eppure il tipo di dati dichiarato è “DataTable”

AGGIORNAMENTO 2

Ho anche provato questo, dopo aver capito che non è realmente un DataTable stringificato / jsonizzato che sto passando dal client, ma un elenco generico jitizzato / stringificato:

CONTROLLORE API WEB

 [Route("{unit}/{begindate}/{enddate}/{stringifiedjsondata}")] [HttpPost] public void Post(string unit, string begindate, string enddate, List stringifiedjsondata) { List _produceUsageList = stringifiedjsondata; 

WebApiConfig.cs

Ho aggiunto questo al metodo Register:

 config.Formatters.Add(new GenericProduceUsageListMediaTypeFormatter()); 

… e questa nuova class:

 // adapted from DataTableMediaTypeFormatter above public class GenericProduceUsageListMediaTypeFormatter : BufferedMediaTypeFormatter { public GenericProduceUsageListMediaTypeFormatter() : base() { SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("test/dt")); } public override object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, System.Threading.CancellationToken cancellationToken) { var data = new StreamReader(readStream).ReadToEnd(); var obj = JsonConvert.DeserializeObject<List>(data); return obj; } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } } 

Tuttavia, tuttavia, la linea principale di breakpoint nel controller:

 List _produceUsageList = stringifiedjsondata; 

… non è stato raggiunto

o versione jsonizzata dei dati (e quindi decomprimerla dall’altra parte

Ho finito per questo –

 public class ParentController : ApiController { public string Post(DataTable id) { return "hello world"; } } 

nella configurazione

 config.Formatters.Add(new DataTableMediaTypeFormatter()); 

E –

 public class DataTableMediaTypeFormatter : BufferedMediaTypeFormatter { public DataTableMediaTypeFormatter() : base() { SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("test/dt")); } public override object ReadFromStream(Type type, System.IO.Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger, System.Threading.CancellationToken cancellationToken) { var data = new StreamReader(readStream).ReadToEnd(); var obj = JsonConvert.DeserializeObject(data); return obj; } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } } header of my request - User-Agent: Fiddler Host: localhost:60957 Content-Type : test/dt Content-Length: 28 

Corpo –

 [{"Name":"Amit","Age":"27"}] 

L’ho già fatto una volta, sebbene il codice sia stato sostituito, quindi sono in grado di ottenere solo frammenti dalla mia cronologia TFS.

Dalla mia app console, avrei inserito i dati (era un DataTable che ho convertito in un POCO) come segue;

  using (HttpClient httpClient = new HttpClient()) { MyDataType data = BogusMethodToPopulateData(); httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyService); httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response; // Add reference to System.Net.Http.Formatting.dll response = await httpClient.PostAsJsonAsync("api/revise", data); if (response.IsSuccessStatusCode) { Console.WriteLine("File generation process completed successfully."); } } 

Sul lato server, ho avuto il seguente. Il concetto qui era in gran parte basato sulla sezione Tipi complessi di invio del post collegato. So che stai guardando DataTable, ma sono sicuro che potresti cazzeggiare con gli esempi o estrarre i tuoi dati in un POCO;

  // https://damienbod.wordpress.com/2014/08/22/web-api-2-exploring-parameter-binding/ // http://www.asp.net/web-api/overview/advanced/sending-html-form-data,-part-1 // http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api [POST("revise")] public IEnumerable Revise(MyDataType data) { if (ModelState.IsValid && data != null) { return ProcessData(data.year, data.period, data.DataToProcess).AsEnumerable(); } return null; } 

Il client sta effettivamente passando una tabella di dati in forma di json, quindi secondo il tipo di supporto speciale runtime di webapi converte nuovamente il json in una tabella di dati sul server.

Vedi la risposta di Hernan Guzman qui .

Fondamentalmente, devi aggiungere “[FromBody]” al metodo sul server e quindi passare i dati dal client, aggiungendolo dopo il parametro URL.