El parámetro de publicación siempre es nulo

Desde que actualicé a RC para WebAPI estoy teniendo algún problema extraño cuando llamo a POST en mi WebAPI. Incluso volví a la versión básica generada en el nuevo proyecto. Asi que:

public void Post(string value) { } 

y llamando desde Fiddler:

 Header: User-Agent: Fiddler Host: localhost:60725 Content-Type: application/json Content-Length: 29 Body: { "value": "test" } 

Cuando depuro, la cadena “valor” nunca se está asignando a. Siempre es NULO. ¿Alguien tiene este problema?

(Primero vi el problema con un tipo más complejo)

El problema no solo está relacionado con ASP.NET MVC 4, sino que también se produce el mismo problema en un proyecto ASP.NET MVC 3 nuevo después de la instalación de RC.

Como solo tiene un parámetro, puede intentar decorarlo con el atributo [FromBody] o cambiar el método para aceptar un DTO con valor como propiedad, como sugerí aquí: enlace de parámetros MVC4 RC WebApi

ACTUALIZACIÓN: El sitio oficial de ASP.NET se actualizó hoy con una excelente explicación: http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-1

En pocas palabras, al enviar un solo tipo simple en el cuerpo, envíe el justo el valor prefijado con un signo igual (=), por ejemplo, cuerpo:

=test

Me he estado rascando la cabeza por esto hoy.

Mi solución es cambiar el [FromBody] a un HttpRequestMessage , esencialmente moviendo hacia arriba la stack HTTP.

En mi caso, estoy enviando datos a través del cable que está comprimido json, que luego se basa64. Todo esto desde una aplicación de Android.

La firma original de mi punto final web se veía así (usando [FromBody] ):

Mi punto final original

Mi solución para este problema fue volver a utilizar un HttpRequestMessage para la firma de mi punto final.

enter image description here

A continuación, puede obtener acceso a los datos de la publicación usando esta línea de código:

enter image description here

Esto funciona y le permite acceder a los datos de publicación intactos sin procesar. No tiene que perder el tiempo con el violín poniendo un signo = al principio de la cadena o cambiando el tipo de contenido.

Como comentario adicional, primero traté de seguir una de las respuestas anteriores que consistía en cambiar el tipo de contenido a: “Content-Type: application / x-www-form-urlencoded”. Para los datos brutos, este es un mal consejo porque elimina + caracteres.

Entonces una cadena base64 que comienza así: “MQ0AAB + LCAAAAAA” termina así “MQ0AAB LCAAAAAA”! No es lo que quieres

Otro beneficio de utilizar HttpRequestMessage es que tiene acceso a todos los encabezados http desde su endpoint.

Me acabo de ocurrir esto usando Fiddler. El problema era que no había especificado Content-Type .

Intente incluir un encabezado para Content-Type en su solicitud POST.

 Content-Type: application/x-www-form-urlencoded 

Alternativamente, según los comentarios a continuación, es posible que deba incluir un encabezado JSON

 Content-Type: application/json 

También me encontré con este problema, y ​​así es como resolví mi problema

código webapi:

 public void Post([FromBody] dynamic data) { string value = data.value; /* do stuff */ } 

codigo del cliente:

 $.post( "webapi/address", { value: "some value" } ); 

Estaba usando Postman y estaba cometiendo el mismo error … pasando el value como objeto json en lugar de cadena

 { "value": "test" } 

Claramente, el anterior es incorrecto cuando el parámetro api es de tipo cadena.

Por lo tanto, solo pase la cadena entre comillas dobles en el cuerpo de la API:

 "test" 

Intente crear una clase que sirva como modelo de datos, luego envíe un objeto JSON con propiedades que coincidan con las propiedades de su clase de modelo de datos. (Nota: He probado esto y funciona con el MVC 4 RC 2012 más reciente que acabo de descargar hoy).

 public HttpResponseMessage Post(ValueModel model) { return Request.CreateResponse(HttpStatusCode.OK, "Value Recieved: " + model.Value); } public class ValueModel { public string Value { get; set; } } 

El objeto JSON siguiente se envía en el cuerpo HTTP-POST, el tipo de contenido es application / json

 { "value": "In MVC4 Beta you could map to simple types like string, but testing with RC 2012 I have only been able to map to DataModels and only JSON (application/json) and url-encoded (application/x-www-form-urlencoded body formats have worked. XML is not working for some reason" } 

Creo que la razón por la que tiene que crear una clase de modelo de datos es porque se supone que los valores simples provienen de los parámetros url, y se supone que un único valor complejo proviene del cuerpo. Tienen los [FromBody] y [FromUrl] , pero usar el [FromBody] string value aún no me funciona. Parece que todavía están resolviendo muchos errores, así que estoy seguro de que esto cambiará en el futuro.

Editar: tengo XML para trabajar en el cuerpo. El serializador XML predeterminado se cambió a DataContractSerializer en lugar de XmlSerializer. Poner la siguiente línea en mi archivo Global.asax solucionó este problema ( referencia )

 GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true; 

Estaba buscando una solución a este problema durante algunos minutos, así que compartiré mi solución.

Si publica un modelo, su modelo debe tener un constructor vacío / predeterminado; de lo contrario, el modelo no se puede crear, obviamente. Tenga cuidado al refactorizar. 😉

Después de algunos bashs, creo que el comportamiento predeterminado es correcto y no hay nada que hackear.

El único truco es: si el argumento de su método de publicación es como el siguiente, debe enviar una cadena simple con comillas dobles en el cuerpo (cuando use ajax o cartero), por ejemplo,

 //send "{\"a\":1}" in body to me, note the outer double quotes [HttpPost("api1")] public String PostMethod1([FromBody]string value) { return "received " + value; // "received {\"a\":1}" } 

De lo contrario, si envía una cadena json en el cuerpo del post sin comillas dobles externas y comillas internas escapadas, entonces debe ser capaz de analizarse a la clase del modelo (el tipo de argumento), por ejemplo, {"a":1, "b":2}

 public class MyPoco{ public int a; public int b; } //send {"a":1, "b":2} in body to me [HttpPost("api2")] public String PostMethod2([FromBody]MyPoco value) { return "received " + value.ToString(); //"received your_namespace+MyPoco" } 

Tuve el mismo problema y descubrí que al cambiar el Tipo de contenido a “aplicación / json” no solucionaba el problema. Sin embargo, “application / json; charset = utf-8” funcionó.

Tuve un problema similar cuando el objeto de solicitud para mi método API web siempre fue nulo. Me di cuenta de que dado que el nombre de la acción del controlador tenía el prefijo “Obtener”, la API web lo trató como un HTTP GET en lugar de como un POST. Después de cambiar el nombre de la acción del controlador, ahora funciona según lo previsto.

Esto funcionó para mí:

  1. Crea una clase C # DTO, con una propiedad para cada atributo que quieras pasar de jQuery / Ajax

     public class EntityData { public string Attr1 { get; set; } public string Attr2 { get; set; } } 
  2. Definir el método de la API web:

     [HttpPost()] public JObject AddNewEntity([FromBody] EntityData entityData) { 
  3. Llame a la API web como tal:

     var entityData = { "attr1": "value1", "attr2": "value2" }; $.ajax({ type: "POST", url: "/api/YOURCONTROLLER/addnewentity", async: true, cache: false, data: JSON.stringify(entityData), contentType: "application/json; charset=utf-8", dataType: "json", success: function (response) { ... } }); 

En mi caso, el problema era que el parámetro era una cadena y no un objeto, cambié el parámetro para que fuera JObject of Newsoft.Json y funciona.

Para aquellos que están teniendo el mismo problema con Swagger o Postman como yo, si está pasando un atributo simple como cadena en una publicación, incluso con el “ContentType” especificado, aún obtendrá un valor nulo.

Pasando solo:

MyValue

Entrará en el controlador como nulo.

Pero si pasa:

“MyValue”

El valor será correcto.

Las citas hicieron la diferencia aquí. Por supuesto, esto es solo para Swagger y Postman. Por ejemplo, en una aplicación Frontend que use Angular, el marco debe resolverlo automáticamente.

Agregar linea

  ValueProviderFactories.Factories.Add(new JsonValueProviderFactory()); 

al final de la función protected void Application_Start() en Global.asax.cs solucionó un problema similar para mí en ASP.NET MVC3.

Con Angular, pude pasar datos en este formato:

  data: '=' + JSON.stringify({ u: $scope.usrname1, p: $scope.pwd1 }), headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' } 

Y en Controlador API Web:

  [HttpPost] public Hashtable Post([FromBody]string jsonString) { IDictionary data = JsonConvert.DeserializeObject>(jsonString); string username = data["u"]; string pwd = data["p"]; ...... 

Alternativamente, también podría publicar datos JSON como este:

  data: { PaintingId: 1, Title: "Animal show", Price: 10.50 } 

Y, en el controlador, acepte un tipo de clase como este:

  [HttpPost] public string POST(Models.PostModel pm) { .... } 

De cualquier manera funciona, si tiene una clase pública establecida en la API, publique JSON; de lo contrario, publique ‘=’ + JSON.stringify ({..: …, ..: …})

Si está utilizando un DataContractSerializer para su Xml Formatter o JSON Formatter, debe deshacerse de él. Lo tengo en mi archivo WebApiConfig:

 public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); var jsonFormatter = config.Formatters.OfType().First(); jsonFormatter.UseDataContractJsonSerializer = true; } 

Simplemente comento jsonFormatter.UseDataContractJsonSerializer = true; y mi parámetro de entrada ya no es nulo. Gracias a ‘Despertar’ por darme una pista.

Sé que esta no es una respuesta a esta pregunta, pero la encontré al buscar una solución a mi problema.

En mi caso, el tipo complejo no estaba enlazado pero no estaba haciendo un POST, estaba haciendo un GET con parámetros de cadena de consulta. La solución fue agregar [FromUri] a la arg:

 public class MyController : ApiController { public IEnumerable Get([FromUri] MyComplexType input) { // input is not null as long as [FromUri] is present in the method arg } } 

Tuve el mismo problema en Fiddler. Ya tenía Content-Type: application/json; charset=utf-8 Content-Type: application/json; charset=utf-8 o Content-Type: application/json en el encabezado de la solicitud.

Mi cuerpo de solicitud también era una cadena simple, y en Fiddler había escrito: {'controller':'ctrl'} . Esto hizo que el parámetro de cadena en mi método POST sea null .

Solución : recuerde usar comillas, indicando una cadena. Es decir, lo arreglé escribiendo "{'controller':'ctrl'}" . (Nota: cuando escribas JSON, asegúrate de usar apóstrofes o escapa de las comillas de esta manera: "{\"controller\":\"ctrl\"}" ).

La forma más sencilla que encontré para tratar el objeto JSON simple que paso a MVC 6 es obtener el tipo de parámetro de publicación como NewtonSoft jObject:

 public ActionResult Test2([FromBody] jObject str) { return Json(new { message = "Test1 Returned: "+ str }); ; } 

La mejor solución para mí es completar el HTTP de la siguiente manera:

 [Route("api/open")] [HttpPost] public async Task open(HttpRequestMessage request) { var json = await request.Content.ReadAsStringAsync(); JavaScriptSerializer jss = new JavaScriptSerializer(); WS_OpenSession param = jss.Deserialize(json); return param.sessionid; } 

y luego deserializar la cadena al objeto que espera en el cuerpo del mensaje. Para mí, WS_OpenSession es una clase que contiene sessionid, usuario y clave.

Desde allí puede usar el objeto param y acceder a sus propiedades.

Muy muy efectivo.

Dije procedente de esta url:

http://bizcoder.com/posting-raw-json-to-web-api

Si está seguro acerca de su JSON enviado, debe rastrear su API cuidadosamente:

  1. Instalar Microsoft.AspNet.WebApi.Tracing paquete Microsoft.AspNet.WebApi.Tracing
  2. Agregar config.EnableSystemDiagnosticsTracing(); en la clase WebApiConfig dentro del método Register .

Ahora mira el resultado de Debug y probablemente encontrarás una entrada de registro de ModelState inválida.

Si ModelState no es válido, puede encontrar la causa real en sus Errors :

Nadie puede adivinar tal excepción:

 Could not load file or assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) 

Para tipos complejos, Web API intenta leer el valor del cuerpo del mensaje, utilizando un formateador de tipo de medio.

Compruebe si tiene algún atributo [Serializable] su clase de modelo.

Elimina el atributo para ver si funciona. Esto funcionó para mí.

Llego un poco tarde a la fiesta, pero cualquier persona que tropiece con un valor NULO pasado al usar un controlador simplemente agrega “=” al frente de su solicitud POST.

El controlador también pasó un valor NULL cuando utilicé la aplicación / json Content-Type. Tenga en cuenta el tipo de contenido “application / x-www-form-urlencoded” a continuación. Sin embargo, el tipo de devolución de la API es “application / json”.

  public static string HttpPostRequest(string url, Dictionary postParameters) { string postData = "="; foreach (string key in postParameters.Keys) { postData += HttpUtility.UrlEncode(key) + "=" + HttpUtility.UrlEncode(postParameters[key]) + ","; } HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url); myHttpWebRequest.Method = "POST"; byte[] data = System.Text.Encoding.ASCII.GetBytes(postData); myHttpWebRequest.ContentType = "application/x-www-form-urlencoded"; myHttpWebRequest.ContentLength = data.Length; Stream requestStream = myHttpWebRequest.GetRequestStream(); requestStream.Write(data, 0, data.Length); requestStream.Close(); HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse(); Stream responseStream = myHttpWebResponse.GetResponseStream(); StreamReader myStreamReader = new StreamReader(responseStream, System.Text.Encoding.Default); string pageContent = myStreamReader.ReadToEnd(); myStreamReader.Close(); responseStream.Close(); myHttpWebResponse.Close(); return pageContent; } 

no importa qué tipo de valor desee publicar, simplemente enciérrelo entre comillas para obtenerlo como una cadena. No para tipos complejos.

javascript:

  var myData = null, url = 'api/' + 'Named/' + 'NamedMethod'; myData = 7; $http.post(url, "'" + myData + "'") .then(function (response) { console.log(response.data); }); myData = "some sentence"; $http.post(url, "'" + myData + "'") .then(function (response) { console.log(response.data); }); myData = { name: 'person name', age: 21 }; $http.post(url, "'" + JSON.stringify(myData) + "'") .then(function (response) { console.log(response.data); }); $http.post(url, "'" + angular.toJson(myData) + "'") .then(function (response) { console.log(response.data); }); 

do#:

  public class NamedController : ApiController { [HttpPost] public int NamedMethod([FromBody] string value) { return value == null ? 1 : 0; } } 

Tuve el mismo problema de obtener un parámetro nulo, pero estaba relacionado con objetos grandes. Resultó que el problema estaba relacionado con la longitud máxima de IIS. Se puede configurar en web.config.

     

Me pregunto por qué la API web suprimió el error y envía objetos nulos a mis API. Encontré el error usando Microsoft.AspNet.WebApi.Tracing.

Corregir el paso de un solo parámetro en el cuerpo a WebAPI funciona con este código $.post(url, { '': productId }

Y atraparlo en acción [HttpPost] public ShoppingCartAddRemoveViewModel Delete([FromBody]string value)

La clave es usar la palabra mágica ‘valor’. Puede ser también int, o algún tipo primitivo. No importa el tipo de contenido o las correcciones de encabezado. El lío es que este código no funciona en la acción de publicación de mvc.

El problema es que su método de acción espera un tipo simple, es decir, un valor de parámetro de cadena. Lo que estás proporcionando es un objeto.

Hay 2 soluciones para su problema.

  1. Cree una clase simple con la propiedad ” valor ” y luego use esa clase como parámetro, en cuyo caso el enlace del modelo de la API Web leerá el objeto JSON de la solicitud y lo vinculará a su propiedad de “valores” del objeto param.

  2. Simplemente pasa el valor de cadena ” prueba “, y funcionará.

Si coloca la anotación [FromBody] y tiene un objeto Dto como parámetro para su método y aún no puede obtener los datos, comience a buscar en las propiedades y campos de su DTO.

Tuve el mismo problema, donde mi DTO era nulo. Descubrí que la razón era que una de las propiedades apuntaba a un objeto que no se puede serializar 🙁 lo que provoca que el formateador de medios no pueda analizar los datos. Por lo tanto, el objeto siempre fue nulo. Espero que también ayude a los demás.

Verifica tus tipos de datos. La carpeta de modelo de dotnet no convertirá una coma flotante en un número entero (y estoy asumiendo otros conceptos relacionados). Esto hará que todo el modelo sea rechazado.

Si tienes json así:

 { "shoeSize": 10.5 } 

pero su modelo c # se ve así:

 class Shoe{ public int shoeSize; } 

el encuadernador modelo rechazará el modelo y obtendrá nulo.

En mi caso, decorar la clase de parámetro con el [JsonObject(MemberSerialization.OptOut)] de Newtonsoft hizo el truco.

Por ejemplo:

 [HttpPost] [Route("MyRoute")] public IHttpActionResult DoWork(MyClass args) { ... } [JsonObject(MemberSerialization.OptOut)] public Class MyClass { ... }