Вопрос по asp.net-web-api, c#, asp.net-web-api-routing, .net – Параметр сообщения всегда равен нулю

186

После обновления до 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"
}

Когда я отлаживаю, строка "значение" никогда не назначается. Это просто всегда NULL. У кого-нибудь есть эта проблема?

(Я впервые увидел проблему с более сложным типом)

Проблема связана не только с ASP.NET MVC 4, такая же проблема возникает для нового проекта ASP.NET MVC 3 после установки RC

Я боролся с этим в течение двух дней, и после прочтения каждой статьи, которую я смог найти, оказалось, что это так же просто, как правильно отформатировать строку JSON в WebRequest: данные должны начинаться и заканчиваться двойными кавычками, а если вы потом используйте одинарные кавычки в ваших данных JSON, все это играет хорошо. Gineer
Просто, чтобы добавить к проблеме - это не только JSON, это также происходит с XML. ianrathbone

Ваш Ответ

30   ответов
12

что поведение по умолчанию правильное и взломать нечего.

Единственный трюк: если ваш аргумент метода poststring как ниже, вы должны отправитьa plain string with double quotes в теле (при использовании ajax или почтальона), например,

//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 в теле сообщенияwithout outer double quotes и избежал внутренних кавычек, тогда он должен быть в состоянии проанализировать с классом модели (тип аргумента), например,{"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"
}
3

что параметр был строкой, а не объектом, я изменил параметр на JObject of Newsoft.Json, и он работает.

2

вы должны тщательно отслеживать свой API:

Install Microsoft.AspNet.WebApi.Tracing package Add config.EnableSystemDiagnosticsTracing(); in the WebApiConfig class inside Register method.

Теперь посмотрите на вывод Debug, и вы, вероятно, найдете недействительный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)
Вы спасли мою жизнь. Получив значение null в качестве модели и после установки пакета, обнаружил, что мой JSON не в правильном формате.
1

который я нашел для работы с простым объектом JSON, который я передаю в MVC 6, - это получить тип параметра post, например, NewtonSoft jObject:

public ActionResult Test2([FromBody] jObject str)
{
        return Json(new { message = "Test1 Returned: "+ str }); ;
}
Тестовое задание([FromBody] object body, [FromHeader(Name="Content-Type")] string bodyMediaType) может работать как проверкаbodyMediaType == "application.json" Перед приведением тела в JObject предусмотрена возможность для альтернатив.
49

Код вебапи:

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

код клиента:

$.post( "webapi/address", { value: "some value" } );
легко, измените свой клиентский код, чтобы отправить обратно объект js.
Это работает, если клиент отправляет JSON. Если я отправляю простое значение, такое как строкаsome value, затемdata нулевой.
Отличный и чистый способ получить данные из JSON
Если вы отправляете json, тогда var json = JsonConvert.SerializeObject (data);
4

когда объект запроса для моего метода Web API всегда был нулевым. Я заметил, что поскольку в имени действия контроллера был указан префикс «Get», Web API воспринимал это как HTTP GET, а не как POST. После переименования действия контроллера оно теперь работает как задумано.

5

Create a C# DTO class, with a property for every attribute you want to pass from jQuery/Ajax

public class EntityData
{
    public string Attr1 { get; set; }
    public string Attr2 { get; set; }
}

Define the web api method:

[HttpPost()]
public JObject AddNewEntity([FromBody] EntityData entityData)
{

Call the web api as such:

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) {
        ...
    }
});
Спасибо за публикацию, я попробовал множество образцов, но это сработало для меня!
1

но у меня возникли похожие проблемы, и после дня, проведенного здесь после получения множества ответов, я нашел самое простое / легкое решение для передачи одного или нескольких параметров в Web API 2. следующим образом:

Это предполагает, что вы знаете, как настроить контроллер / действие Web 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
 });

Ключевой проблемой, которую я обнаружил, было убедиться, что вы отправляете только один общий параметр обратно в Web API и убедитесь, что у него нет имени, только значение{ '': dataToSend }в противном случае ваше значение будет нулевым на стороне сервера.

При этом вы можете отправить один или несколько параметров в Web API в виде структуры JSON, и вам не нужно объявлять какие-либо дополнительные объекты на стороне сервера для обработки сложных данных. JObject также позволяет вам динамически перебирать все передаваемые параметры, обеспечивая более легкую масштабируемость, если ваши параметры изменяются со временем. Надеюсь, это поможет кому-то, кто боролся, как я.

2

Если вы используете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;и мой входной параметр больше не равен нулю. Благодаря «Despertar» что дал мне подсказку.

1

что это не ответ на этот вопрос, но я столкнулся с ним при поиске решения моей проблемы.

В моем случае сложный тип не был связан, но я не делал POST, я делал GET с параметрами строки запроса. Решением было добавить [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
    }
}
Это потому, что тело запросаignored (и иногда вызывает ошибку) при использовании GET. Причина, по которой это работает сейчас, заключается в том, что вы передаете параметры в строке запроса, а ваше тело пустоas should be on a GET
18

который будет служить моделью данных, а затем отправьте объект 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. Помещение следующей строки в мой файл Global.asax устранило эту проблему (ссылка)

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

Я использовалPostman и я делал ту же ошибку .. прохождениеvalue как объект JSON вместо строки

{
    "value": "test"
}

Ясно, что вышеwrong когда параметр api имеет тип string.

Итак, просто передайте строку в двойных кавычках в теле API:

"test"
Это сработало для меня. Используя Swagger (swashbuckle), я должен установить тип контента: application / json и использовать двойные кавычки.
Именно моя проблема. Но я должен отметить, что в Ajax-запросах вы должны указывать «данные: JSON.stringify (& quot; YourString & quot;)» apos;
@AmirHosseinAhmadi Я только что столкнулся с этим на ajax и увидел, что на самом деле с помощьюJSON.stringify вызывал мой[frombody] бытьnull, После установки в поле данных строкового значения (то есть строки json) это сработало.
Это была моя проблема, и, вероятно, реальное решение вопроса. Строка в кодировке JSON содержит кавычки
Я также столкнулся с этой проблемой на Почтальоне. Оказывается, я неправильно выбрал «form-data» вместо «сырого» для Body, когда Content-Type установлен на application / json. Вы можете сослаться на скриншоты zzyykk123456 в выпусках aspnet Github:github.com/aspnet/Home/issues/2202
1

но любой, кто наткнулся на значение NULL, переданное при использовании контроллера, просто добавляет & quot; = & quot; в начале вашего запроса POST.

Контроллер также передал значение NULL, когда я использовал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;
    }
69

используя Fiddler. Проблема заключалась в том, что я не указалContent-Type.

Попробуйте включить заголовок дляContent-Type в вашем запросе POST.

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

В качестве альтернативы, согласно комментариям ниже, вам может понадобиться включить заголовок JSON

Content-Type: application/json
application / x-www-form-urlencoded у меня не работает, Content-Type: application / json сделал.
У меня была похожая проблема, за исключением того, что мне нужно было установить Content-Type: application / json
Я должен был использоватьcontentType: "application/x-www-form-urlencoded; charset=UTF-8" , для полного примера см.Complete Cient and Server
Независимо от типа содержимого, если у вас есть только один параметр, вы должны отправить только значение без имени параметра в теле запроса. Таким образом, id = 13 не будет работать. Вам нужно отправить 13 в одиночку. Смотрите ответ Джима.
1

[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;
}

и затем десериализацию строки в объект, который вы ожидаете в теле сообщения. Для меня WS_OpenSession - это класс, который содержит sessionid, user и key.

Оттуда вы можете использовать объект param и получить доступ к его свойствам.

Очень очень эффективно.

Я сказал источник из этого URL:

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

4

Swagger или жеPostman как я сделал, если вы передаете простой атрибут в виде строки в сообщении, даже с & quot; ContentType & quot; указан, вы все равно получите нулевое значение.

Проходя просто:

MyValue

Попадет в контроллер как ноль.

Но если вы пройдете:

"MyValue"

Значение станет правильным.

Цитаты сделали разницу здесь. Конечно, это только для Сваггера и Почтальона. Например, в приложении Frontend, использующем Angular, это должно решаться платформой автоматически.

2

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

до конца функцииprotected void Application_Start() в Global.asax.cs исправлена аналогичная проблема для меня в ASP.NET MVC3.

Спасибо за помощь, но, к сожалению, это не помогло. Я буду держать линию на некоторое время, так как она может помочь в любом случае! ianrathbone
Просто чтобы добавить те же проблемы возникают, когда я публикую XML ianrathbone
1

какой тип значения вы хотите опубликовать, просто заключите его в кавычки, чтобы получить его в виде строки. Не для сложных типов.

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;
        }
    }
0

$.post(url, { '': productId }

И ловить это в действии[HttpPost] public ShoppingCartAddRemoveViewModel Delete([FromBody]string value)

Ключ в том, чтобы использовать магическое слово «значение». Это может быть также int или некоторый примитивный тип. Независимо от типа содержимого или исправления заголовка Беспорядок в том, что этот код не работает в mvc post action.

1

Content-Type: application/json; charset=utf-8 или жеContent-Type: application/json в заголовке запроса.

Моим телом запроса была также простая строка, и в Fiddler я написал:{'controller':'ctrl'}, Это сделало строковый параметр в моем методе POSTnull.

Fix: не забывайте использовать кавычки, тем самым обозначая строку. То есть я исправил это написав"{'controller':'ctrl'}", (Примечание: при написании JSON обязательно используйте апострофы или экранируйте кавычки следующим образом:"{\"controller\":\"ctrl\"}").

Я должен был сделать то же самое в Почтальоне. Но мне не нужно это делать, когда я звоню в Spring. Похоже, проблема на стороне .Net.
0

что ваш метод действия ожидает простой тип, то есть значение строкового параметра. То, что вы предоставляете, является объектом.

There are 2 solutions to your problem.

Create a simple class with "value" property and then use that class as parameter, in which case Web API model binding will read JSON object from request and bind it to your param object "values" property.

Just pass string value "test", and it will work.

94

вы можете попытаться украсить его[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] водоворот, но это не изменилось ianrathbone
Передайте только значение (т.е. не как объект JSON), и оно должно работать согласноblogs.msdn.com/b/jmstall/archive/2012/04/16/…
[FromBody] работает для нас, но, на наш взгляд, это шаг назад. С бета-версией мы могли иметь несколько простых аргументов типа, и они были очень хорошо связаны с методами в нашемApiControllers. Так что, хотя объяснение, на которое вы ссылаетесь, действительно объясняет, я категорически не согласен :-)
Приложение= передо мной никогда не работал, пока я не последовал совету Джима в его комментарии (не как объект JSON), и это сработало. Это ключ! Мне действительно не нравится, насколько требователен WebAPI.
Это настолько глупо и раздражает, что я не знаю, стоит ли повышать или понижать ответ, который помог мне решить мою проблему ... С какой стати это должно быть в этом формате? (Извините за отношение, просто потратил слишком много времени на это, и это не имеет никакого смысла ...:( ). Вы должны действительно добавить поддержку формата, который люди ожидают, что он примет.
1

я, используя средство форматирования медиа.

Пожалуйста, проверьте, если у вас есть[Serializable] атрибут, украшающий вашу модель класса.

Удалите атрибут, чтобы увидеть, работает ли он. Это сработало для меня.

Это сработало !! Спасибо
96

Мое решение состоит в том, чтобы изменить[FromBody] кHttpRequestMessage, по сути, движется вверх по стеку HTTP.

В моем случае я посылаю данные по проводу, который является zips json, который затем base64 'd. Все это из приложения для Android.

Исходная подпись моей конечной точки в сети выглядела так (используя[FromBody]) :

My original endpoint

Мое исправление для этой проблемы состояло в том, чтобы вернуться к использованиюHttpRequestMessage для подписи моей конечной точки.

enter image description here

Затем вы можете получить доступ к данным поста, используя следующую строку кода:

enter image description here

Это работает и позволяет вам получить доступ к необработанным нетронутым данным поста. Вам не нужно возиться с Fiddler, который ставит знак = в начале вашей строки или изменяет тип контента.

Кроме того, сначала я попытался выполнить один из приведенных выше ответов, который состоял в том, чтобы изменить тип контента на «Content-Type: application / x-www-form-urlencoded». Для необработанных данных это плохой совет, потому что он убирает символы +.

Таким образом, строка base64 начинается следующим образом: & quot; MQ0AAB + LCAAAAAA & quot; заканчивается так "MQ0AAB LCAAAAAA"! Не то, что вы хотите.

Еще одно преимущество использованияHttpRequestMessage является то, что вы получаете доступ ко всем заголовкам http из вашей конечной точки.

Отличное решение ... у меня ушло 9 часов, чтобы найти это, я решил проблему после того, как удалил строковое значение [FromBody] и заменил его на HttpRequestMessage
Работает как шарм! но нет ли способа использовать фактический тип параметра определенного объекта? например, параметр типа Order для метода веб-API?
Просто примечание, вам не нужноHttpRequestMessage request в сигнатуре метода, как у вас всегда есть это уже. В теле метода он доступен наRequest объект. напримерRequest.Content.ReadAsStringAsync().Result;
2

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

А в Web 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, в противном случае отправьте & apos; = & apos; + JSON.stringify ({..: ..., ..: ...})

11

поэтому поделюсь своим решением.

Если вы публикуете модель, ваша модель должна иметь пустой конструктор / конструктор по умолчанию, в противном случае модель, очевидно, не может быть создана. Будьте осторожны при рефакторинге. ;)

Рефакторинг - это то, что меня здесь поразило, спасибо за совет
0

е преобразует число с плавающей точкой в целое число (и я предполагаю другие связанные понятия). Это приведет к отклонению всей модели.

Если у вас есть JSON, как это:

{
    "shoeSize": 10.5
}

но ваша модель c # выглядит так:

class Shoe{
    public int shoeSize;
}

связыватель модели отклонит модель, и вы получите ноль.

0

ве параметра для вашего метода, но вы все еще не можете получить данные, начните изучать свойства и поля вашего DTO.

У меня была такая же проблема, когда мой DTO обнулялся. Я нашел причину в том, что одно из свойств указывало наobject that cannot be serialised :( что приводит к тому, что медиа-форматер не может проанализировать данные. Таким образом, объект всегда был нулевым. Надеюсь, что это помогает другим

5

и я обнаружил, что при изменении типа контента на «application / json» не решил проблему. Однако & quot; application / json; кодировка = UTF-8 & Quot; работал.

Да, Web API не будет десериализован без charset = utf-8.blog.codenamed.nl/2015/05/12/…
1

метра, но это было связано с большими объектами. Оказалось, что проблема связана с максимальной длиной IIS. Это можно настроить в web.config.

  <system.web>
    <httpRuntime targetFramework="4.7" maxRequestLength="1073741824" />
  </system.web>

Интересно, почему Web API подавил ошибку и отправляет нулевые объекты в мои API. Я нашел ошибку с помощью Microsoft.AspNet.WebApi.Tracing.

Похожие вопросы