Параметр Post всегда имеет значение null
С момента обновления до RC для WebAPI у меня возникает реальная странная проблема при вызове POST на моем WebAPI. Я даже вернулся к базовой версии, созданной в новом проекте. Итак:
public void Post(string value)
{
}
и звонок от Саши:
Header:
User-Agent: Fiddler
Host: localhost:60725
Content-Type: application/json
Content-Length: 29
Body:
{
"value": "test"
}
когда я отлаживаю, строка "value" никогда не назначается. Это просто всегда NULL. У кого-нибудь есть эта проблема?
(Я впервые увидел проблему с более сложным типом)
проблема не только связана с ASP.NET MVC 4, такая же проблема происходит для свежего ASP.NET проект MVC 3 после установки RC
30 ответов
поскольку у вас есть только один параметр, вы можете попробовать украсить его с помощью [FromBody]
атрибут или измените метод, чтобы принять DTO со значением в качестве свойства, как я предложил здесь:привязка параметров MVC4 RC WebApi
обновление: официальный ASP.NET сайт был обновлен сегодня с отличным объяснением: http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-1
в двух словах, при отправке один простой введите тело, отправьте только значение с префиксом равенства ( = ), например, тело:
=test
Я сегодня чесал голову над этим.
мое решение-менять [FromBody]
до HttpRequestMessage
, по существу перемещаясь вверх по стеку HTTP.
в моем случае я отправляю данные по проводу, который застегивается json, который затем base64'D. Все это из приложения для android.
исходная подпись моей веб-конечной точки выглядела так (используя [FromBody]
):
мое исправление для этой проблемы состояло в том, чтобы вернуться к использованию HttpRequestMessage
для подписи моей конечной точки.
вы можете получить доступ к данным post, используя эту строку кода:
это работает и позволяет получить доступ к необработанным нетронутым данным post. Вам не нужно возиться с fiddler, помещая знак = в начале строки или изменяя тип содержимого.
в стороне, я сначала попытался следовать одному из ответов выше, который должен был изменить тип контента: "тип контента: application / x-www-form-urlencoded". Для необработанных данных это плохой совет, потому что он удаляет символы+.
Итак, строка base64, которая начинается так:" MQ0AAB+LCAAAAAA "заканчивается так:"MQ0AAB LCAAAAAA"! Не то, что ты хочешь.
еще одно преимущество использования HttpRequestMessage
это то, что вы получаете доступ ко всем заголовкам http из своей конечной точки.
у меня только что это произошло с помощью Fiddler. Проблема была в том, что я не уточнил!--2-->.
попробуйте включить заголовок для Content-Type
в POST-запрос.
Content-Type: application/x-www-form-urlencoded
в качестве альтернативы, согласно комментариям ниже, вам может потребоваться включить заголовок json
Content-Type: application/json
Я тоже столкнулся с этой проблемой, и вот как я решил свою проблему
код webapi:
public void Post([FromBody] dynamic data)
{
string value = data.value;
/* do stuff */
}
клиентский код:
$.post( "webapi/address", { value: "some value" } );
Я был с помощью Postman
и я делал ту же ошибку.. проходя мимо value
как объект json вместо string
{
"value": "test"
}
ясно, что выше один неправильно когда параметр api имеет тип string.
Итак, просто передайте строку в двойных кавычках в теле api:
"test"
попробуйте создать класс, который будет служить моделью данных, а затем отправьте объект JSON со свойствами, соответствующими свойствам класса модели данных. (Примечание: я протестировал это, и он работает с новейшим MVC 4 RC 2012, который я только что загрузил сегодня).
public HttpResponseMessage Post(ValueModel model)
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "Value Recieved: " + model.Value);
}
public class ValueModel
{
public string Value { get; set; }
}
приведенный ниже объект JSON отправляется в теле HTTP-POST, тип контента-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" }
Я считаю, что причина, по которой вам нужно создать класс модели данных, заключается в том, что предполагается, что простые значения предполагается, что параметры url и одно комплексное значение относятся к телу. У них есть [FromBody]
и [FromUrl]
атрибуты, но с помощью [FromBody] string value
все еще не работал на меня. Похоже, они все еще разрабатывают много ошибок, поэтому я уверен, что это изменится в будущем.
Edit: Получил XML для работы в теле. Сериализатор XML по умолчанию был изменен на DataContractSerializer вместо XmlSerializer. Вставляю следующую строку в мой Глобал.файл asax исправил это выпуск (ссылка)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
Я искал решение этой проблемы в течение нескольких минут, поэтому я поделюсь своим решением.
Если вы публикуете модель, ваша модель должна иметь пустой конструктор / default, иначе модель не может быть создана, очевидно. Будьте осторожны при рефакторинге. ;)
после некоторых попыток, я думаю, что поведение по умолчанию является правильным и нечего рубить.
единственный трюк: если ваш аргумент метода post является string
, как показано ниже, вы должны отправить простая строка с двойными кавычками в теле (при использовании ajax или postman), например,
//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}"
}
в противном случае, если вы отправляете строку json в теле post без внешних двойных кавычек и скрылся внутренней котировки, то он должен быть вставлен класс модели (тип аргумента), например,{"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"
}
У меня была такая же проблема, и я обнаружил, что при изменении типа контента на "application/json" проблема не устранена. Однако" application/json; charset=utf-8 " работал.
У меня была аналогичная проблема, когда объект запроса для моего метода Web API всегда был нулевым. Я заметил, что, поскольку имя действия контроллера было префиксом "Get", веб-API рассматривал это как HTTP GET, а не POST. После переименования действия контроллера он теперь работает по назначению.
Это сработало для меня:
-
создайте класс C# DTO со свойством для каждого атрибута, который вы хотите передать из jQuery/Ajax
public class EntityData { public string Attr1 { get; set; } public string Attr2 { get; set; } }
-
определите метод Web api:
[HttpPost()] public JObject AddNewEntity([FromBody] EntityData entityData) {
-
вызовите веб-api как таковой:
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) { ... } });
в моем случае проблема заключалась в том, что параметр был строкой, а не объектом, я изменил параметр на JObject Newsoft.Json и это работает.
для тех, у кого такая же проблема с Swagger или Postman, как у меня, если вы передаете простой атрибут в виде строки в сообщении, даже с указанным "ContentType", вы все равно получите значение null.
пройдя всего:
значения MyValue
попадет в контроллер как null.
но если вы проходите:
"MyValue"
значение будет получить права.
котировки сделали разницу здесь. Конечно, это только для Суэггера и почтальона. Например, в интерфейсном приложении с использованием Angular это должно быть разрешено фреймворком автоматически.
добавить строку
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
до конца функции protected void Application_Start()
в глобальном.асакс.cs исправил аналогичную проблему для меня в ASP.NET MVC3.
С угловым, я смог передать данные в этом формате:
data: '=' + JSON.stringify({ u: $scope.usrname1, p: $scope.pwd1 }),
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' }
и в веб-API Controler:
[HttpPost]
public Hashtable Post([FromBody]string jsonString)
{
IDictionary<string, string> data = JsonConvert.DeserializeObject<IDictionary<string, string>>(jsonString);
string username = data["u"];
string pwd = data["p"];
......
кроме того, я мог бы также опубликовать данные JSON следующим образом:
data: { PaintingId: 1, Title: "Animal show", Price: 10.50 }
и в контроллере примите такой тип класса:
[HttpPost]
public string POST(Models.PostModel pm)
{
....
}
в любом случае работает, если у вас есть установленный публичный класс в API, затем опубликуйте JSON, иначе post '=' + JSON.преобразовать в строки({..: ..., .. : ... })
Если вы используете DataContractSerializer для вашего XML Formatter или JSON Formatter вам нужно избавиться от него. У меня было это в моем файле 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<JsonMediaTypeFormatter>().First();
jsonFormatter.UseDataContractJsonSerializer = true;
}
просто я закомментировать jsonFormatter.UseDataContractJsonSerializer = true;
и мой входной параметр не равен null. Благодаря 'Despertar' дал мне подсказку.
Я знаю, что это не ответ на этот вопрос, но я наткнулся на это при поиске решения моей проблемы.
в моем случае сложный тип не был связан, но я не делал POST, я делал GET с параметрами querystring. Решение состояло в том, чтобы добавить [FromUri] в arg:
public class MyController : ApiController
{
public IEnumerable<MyModel> Get([FromUri] MyComplexType input)
{
// input is not null as long as [FromUri] is present in the method arg
}
}
у меня была такая же проблема в Fiddler. У меня уже было Content-Type: application/json; charset=utf-8
или Content-Type: application/json
в заголовке запроса.
мое тело запроса также было простой строкой, и в Fiddler я написал:{'controller':'ctrl'}
. Это сделало параметр string в моем методе POST null
.
исправить: не забудьте использовать кавычки, тем самым указывая строку. То есть, я исправил это, написав "{'controller':'ctrl'}"
. (Примечание: при написании JSON обязательно используйте апострофы или избегайте кавычек вот так:"{\"controller\":\"ctrl\"}"
).
самый простой способ, который я нашел для работы с простым объектом JSON, который я передаю в MVC 6, - это получить тип параметра post, например NewtonSoft jObject:
public ActionResult Test2([FromBody] jObject str)
{
return Json(new { message = "Test1 Returned: "+ str }); ;
}
лучшее решение для меня - это полный HTTP, как показано ниже:
[Route("api/open")]
[HttpPost]
public async Task<string> open(HttpRequestMessage request)
{
var json = await request.Content.ReadAsStringAsync();
JavaScriptSerializer jss = new JavaScriptSerializer();
WS_OpenSession param = jss.Deserialize<WS_OpenSession>(json);
return param.sessionid;
}
а затем десериализация строки в объект, который вы ожидаете в теле post. Для меня WS_OpenSession-это класс, содержащий sessionid, user и key.
оттуда вы можете использовать объект param и получить доступ к его свойствам.
очень очень эффективным.
Я сказал, что источник из этого URL-адрес:
если вы уверены в отправленном JSON, вы должны тщательно отслеживать свой API:
- установить
Microsoft.AspNet.WebApi.Tracing
пакета - добавить
config.EnableSystemDiagnosticsTracing();
наWebApiConfig
внутри классаRegister
метод.
теперь посмотрите на вывод отладки, и вы, вероятно, найдете недопустимый ModelState
запись в журнале.
если ModelState
недействителен вы можете найти реальную причину в его Errors
:
никто даже не догадывается о таком исключении:
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)
для сложных типов Web API пытается прочитать значение из тела сообщения, используя форматер типа носителя.
пожалуйста, проверьте, если у вас есть какие-либо [Serializable]
атрибут, украшающий ваш класс модели.
удалите атрибут, чтобы узнать, работает ли он. Это сработало для меня.
Я немного опаздываю на вечеринку, но любой, кто натыкается на нулевое значение, переданное при использовании контроллера, просто добавляет "=" к передней части вашего запроса POST.
контроллер также передал нулевое значение, когда я использовал application / json Контент-Тип. Обратите внимание на тип контента" application/x-www-form-urlencoded " ниже. Однако тип возврата из API-интерфейса - "application / json".
public static string HttpPostRequest(string url, Dictionary<string, string> 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;
}
не имеет значения, какой тип значения вы хотите опубликовать, просто заключите его в кавычки, чтобы получить его как строку. Не для сложных типов.
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); });
c#:
public class NamedController : ApiController
{
[HttpPost]
public int NamedMethod([FromBody] string value)
{
return value == null ? 1 : 0;
}
}
У меня была та же проблема получения null в качестве параметра, но она была связана с большими объектами. Оказалось, что проблема связана с максимальной длиной IIS. Его можно настроить в web.конфиг.
<system.web>
<httpRuntime targetFramework="4.7" maxRequestLength="1073741824" />
</system.web>
интересно, почему Web API подавил ошибку и отправляет нулевые объекты в мои API. Я нашел ошибку с помощью Microsoft.сеть САШ.Веб-API.Трассировка.
Я довольно поздно к этому, но имел аналогичные проблемы, и после дня прохождения через много ответов здесь и получения фона я нашел самое простое / легкое решение для передачи одного или нескольких параметров в действие Web API 2 следующим образом:
это предполагает, что вы знаете, как настроить контроллер/действие веб-API с правильной маршрутизацией, если не обратитесь к: https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api.
сначала действие контроллера, это решение также требует Newtonsoft.Библиотека Json.
[HttpPost]
public string PostProcessData([FromBody]string parameters) {
if (!String.IsNullOrEmpty(parameters)) {
JObject json = JObject.Parse(parameters);
// Code logic below
// Can access params via json["paramName"].ToString();
}
return "";
}
клиентская сторона с помощью jQuery
var dataToSend = JSON.stringify({ param1: "value1", param2: "value2"...});
$.post('/Web_API_URI', { '': dataToSend }).done(function (data) {
console.debug(data); // returned data from Web API
});
ключевой проблемой, которую я нашел, было то, что вы отправляете только один общий параметр обратно в веб-API и убедитесь, что у него нет имени только значение { '': dataToSend }
в противном случае ваши ценности будут быть null на стороне сервера.
С этим вы можете отправить один или несколько параметров в веб-API в структуре JSON, и вам не нужно объявлять какие-либо дополнительные объекты на стороне сервера для обработки сложных данных. JObject также позволяет динамически перебирать все переданные параметры, что позволяет упростить масштабируемость, если ваши параметры изменяются с течением времени. Надеюсь, это поможет кому-то, кто боролся, как я.
корректная передача одного параметра в тело WebAPI работает этот код $.post(url, { '': productId }
и поймать его в действии [HttpPost] public ShoppingCartAddRemoveViewModel Delete([FromBody]string value)
ключ должен использовать волшебное слово "значение". Это может быть также int или какой-то примитивный тип. Независимо от типа контента или исправлений заголовка Беспорядок в том, что этот код не работает в MVC post action.
проблема в том, что ваш метод действия ожидает простой тип, т. е. значение строкового параметра. То, что вы предоставляете, является объектом.
есть 2 решения вашей проблемы.
создайте простой класс с помощью"стоимостью " свойство, а затем использовать этот класс в качестве параметра, в этом случае привязка модели Web API будет считывать объект JSON из запроса и привязывать его к вашему объекту param "значения" свойство.
просто передать строку "тест", и это сработает.
Если вы поместили аннотацию [FromBody] и у вас есть объект Dto в качестве параметра вашего метода и все еще не можете получить данные, начните просматривать свойства и поля вашего DTO.
У меня была такая же проблема, где мой DTO становился нулевым. Я нашел причину в том, что одно из свойств указывало на объект, который не может быть сериализован :( который вызывает в медиа-праматерии с ошибкой для анализа данных. Таким образом, объект был всегда имеет значение null. Надеюсь, это поможет другим тоже
дважды проверьте типы данных. Связующее устройство модели dotnet не преобразует float в целое число (и я предполагаю другие связанные понятия). Это приведет к отклонению всей модели.
Если у вас есть json, как это:
{
"shoeSize": 10.5
}
но ваша модель c# выглядит так:
class Shoe{
public int shoeSize;
}
связыватель модели отклонит модель, и вы получите null.