Parámetros de colocación / publicación múltiple de WebAPI

Estoy tratando de publicar múltiples parámetros en un controlador WebAPI. Un parámetro es de la URL y el otro del cuerpo. Aquí está la url: /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

Aquí está mi código de controlador:

 public HttpResponseMessage Put(Guid offerId, OfferPriceParameters offerPriceParameters) { //What!? var ser = new DataContractJsonSerializer(typeof(OfferPriceParameters)); HttpContext.Current.Request.InputStream.Position = 0; var what = ser.ReadObject(HttpContext.Current.Request.InputStream); return new HttpResponseMessage(HttpStatusCode.Created); } 

El contenido del cuerpo está en JSON:

 { "Associations": { "list": [ { "FromEntityId":"276774bb-9bd9-4bbd-a7e7-6ed3d69f196f", "ToEntityId":"ed0d2616-f707-446b-9e40-b77b94fb7d2b", "Types": { "list":[ { "BillingCommitment":5, "BillingCycle":5, "Prices": { "list":[ { "CurrencyId":"274d24c9-7d0b-40ea-a936-e800d74ead53", "RecurringFee":4, "SetupFee":5 }] } }] } }] } } 

¿Alguna idea de por qué el enlace predeterminado no se puede vincular al argumento offerPriceParameters de mi controlador? Siempre se establece en nulo. Pero puedo recuperar los datos del cuerpo usando DataContractJsonSerializer .

También trato de usar el atributo FromBody del argumento, pero tampoco funciona.

EDITAR: Si está utilizando WebAPI 2 (y con suerte lo estará si está leyendo esto después de hacer esta edición), consulte http://www.asp.net/web-api/overview/formats-and-model- binding / parameter-binding-in-aspnet-web-api .

No puedes hacer eso con WebAPI. Consulte http://www.west-wind.com/weblog/posts/2012/May/08/Passing-multiple-POST-parameters-to-Web-API-Controller-Methods para obtener una visión detallada de este, junto con algunos soluciones útiles.

Si busca “del cuerpo”, también encontrará un comentario que trate específicamente de su bash alternativo.

Nativamente WebAPI no admite el enlace de múltiples parámetros POST. Como señala Colin, hay una serie de limitaciones que se detallan en la publicación de mi blog a la que hace referencia.

Hay una solución al crear una carpeta de parámetros personalizada. El código para hacer esto es feo y complicado, pero he publicado el código junto con una explicación detallada en mi blog, listo para ser conectado a un proyecto aquí:

Pasar múltiples valores POST simples a ASP.NET Web API

 [HttpPost] public string MyMethod([FromBody]JObject data) { Customer customer = data["customerData"].ToObject(); Product product = data["productData"].ToObject(); Employee employee = data["employeeData"].ToObject(); //... other class.... } 

usando referance

 using Newtonsoft.Json.Linq; 

Utilice la solicitud de JQuery Ajax

 var customer = { "Name": "jhon", "Id": 1, }; var product = { "Name": "table", "CategoryId": 5, "Count": 100 }; var employee = { "Name": "Fatih", "Id": 4, }; var myData = {}; myData.customerData = customer; myData.productData = product; myData.employeeData = employee; $.ajax({ type: 'POST', async: true, dataType: "json", url: "Your Url", data: myData, success: function (data) { console.log("Response Data ↓"); console.log(data); }, error: function (err) { console.log(err); } }); 

Si se utiliza el enrutamiento de atributos, puede usar los atributos [FromUri] y [FromBody].

Ejemplo:

 [HttpPost()] [Route("api/products/{id:int}")] public HttpResponseMessage AddProduct([FromUri()] int id, [FromBody()] Product product) { // Add product } 

Pasamos el objeto Json por el método HttpPost y lo analizamos en un objeto dynamic. funciona bien. este es el código de muestra:

 ajaxPost: ... Content-Type: application/json, data: {"AppName":"SamplePrice", "AppInstanceID":"100", "ProcessGUID":"072af8c3-482a-4b1c‌​-890b-685ce2fcc75d", "UserID":"20", "UserName":"Jack", "NextActivityPerformers":{ "39‌​c71004-d822-4c15-9ff2-94ca1068d745":[{ "UserID":10, "UserName":"Smith" }] }} ... 

webapi:

 [HttpPost] public string DoJson2(dynamic data) { //whole: var c = JsonConvert.DeserializeObject(data.ToString()); //or var c1 = JsonConvert.DeserializeObject< ComplexObject1 >(data.c1.ToString()); var c2 = JsonConvert.DeserializeObject< ComplexObject2 >(data.c2.ToString()); string appName = data.AppName; int appInstanceID = data.AppInstanceID; string processGUID = data.ProcessGUID; int userID = data.UserID; string userName = data.UserName; var performer = JsonConvert.DeserializeObject< NextActivityPerformers >(data.NextActivityPerformers.ToString()); ... } 

El tipo de objeto complejo podría ser objeto, matriz y diccionario.

Puede permitir múltiples parámetros POST utilizando la clase MultiPostParameterBinding de https://github.com/keith5000/MultiPostParameterBinding

Para usarlo:

1) Descargue el código en la carpeta de origen y agréguelo a su proyecto de API web o a cualquier otro proyecto en la solución.

2) Use el atributo [MultiPostParameters] en los métodos de acción que necesitan soportar múltiples parámetros POST.

 [MultiPostParameters] public string DoSomething(CustomType param1, CustomType param2, string param3) { ... } 

3) Agregue esta línea en Global.asax.cs al método Application_Start en cualquier lugar antes de la llamada a GlobalConfiguration.Configure (WebApiConfig.Register) :

 GlobalConfiguration.Configuration.ParameterBindingRules.Insert(0, MultiPostParameterBinding.CreateBindingForMarkedParameters); 

4) Haga que sus clientes pasen los parámetros como propiedades de un objeto. Un ejemplo de objeto JSON para el DoSomething(param1, param2, param3) es:

 { param1:{ Text:"" }, param2:{ Text:"" }, param3:"" } 

Ejemplo JQuery:

 $.ajax({ data: JSON.stringify({ param1:{ Text:"" }, param2:{ Text:"" }, param3:"" }), url: '/MyService/DoSomething', contentType: "application/json", method: "POST", processData: false }) .success(function (result) { ... }); 

Visita el enlace para más detalles.

Descargo de responsabilidad: estoy directamente asociado con el recurso vinculado.

Se puede usar una clase de parámetro simple para pasar múltiples parámetros en una publicación:

 public class AddCustomerArgs { public string First { get; set; } public string Last { get; set; } } [HttpPost] public IHttpActionResult AddCustomer(AddCustomerArgs args) { //use args... return Ok(); } 

Buena pregunta y comentarios: aprendí mucho de las respuestas aquí 🙂

Como ejemplo adicional, tenga en cuenta que también puede mezclar cuerpo y rutas, por ejemplo

 [RoutePrefix("api/test")] public class MyProtectedController { [Authorize] [Route("id/{id}")] public IEnumerable Post(String id, [FromBody] JObject data) { /* id = "123" data.GetValue("username").ToString() = "user1" data.GetValue("password").ToString() = "pass1" */ } } 

Llamando así:

 POST /api/test/id/123 HTTP/1.1 Host: localhost Accept: application/json Content-Type: application/x-www-form-urlencoded Authorization: Bearer xyz Cache-Control: no-cache username=user1&password=pass1 enter code here 

¿Cómo se ve tu routeTemplate para este caso?

Publicaste esta url:

 /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/ 

Para que esto funcione, esperaría un enrutamiento como este en su WebApiConfig :

 routeTemplate: {controller}/{offerId}/prices/ 

Otras suposiciones son: – su controlador se llama OffersController . – el objeto JSON que está pasando en el cuerpo de la solicitud es de tipo OfferPriceParameters (no cualquier tipo derivado) – no tiene otros métodos en el controlador que puedan interferir con este (si lo hace, intente comentarlos y vea lo que pasa)

Y como Filip mencionó, ayudaría con sus preguntas si comenzara a aceptar algunas respuestas, ya que ‘aceptar tasa de 0%’ podría hacer que las personas piensen que están perdiendo el tiempo

Si no quiere usar el modo ModelBinding, puede usar DTO para hacer esto por usted. Por ejemplo, cree una acción POST en DataLayer que acepte un tipo complejo y envíe datos desde BusinessLayer. Puedes hacerlo en caso de una llamada UI-> API.

Aquí hay muestra DTO. Asignar un profesor a un alumno y asignar varios documentos / asignatura al alumno.

 public class StudentCurriculumDTO { public StudentTeacherMapping StudentTeacherMapping { get; set; } public List Paper { get; set; } } public class StudentTeacherMapping { public Guid StudentID { get; set; } public Guid TeacherId { get; set; } } public class Paper { public Guid PaperID { get; set; } public string Status { get; set; } } 

Entonces la acción en el DataLayer se puede crear como:

 [HttpPost] [ActionName("MyActionName")] public async Task InternalName(StudentCurriculumDTO studentData) { //Do whatever.... insert the data if nothing else! } 

Para llamarlo desde BusinessLayer:

 using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", dataof_StudentCurriculumDTO) { //Do whatever.... get response if nothing else! } 

Ahora esto funcionará si quiero enviar datos de varios estudiantes a la vez. Modifique MyAction como a continuación. No es necesario escribir [FromBody], WebAPI2 toma el tipo complejo [FromBody] de forma predeterminada.

 public async Task InternalName(List studentData) 

y luego al llamarlo, pase una List de datos.

 using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", List)