Постоянный параметр всегда равен нулю.

С момента обновления до RC для WebAPI у меня возникла какая-то непонятная проблема при вызове POST на моем WebAPI. Я даже вернулся к базовой версии, сгенерированной в новом проекте. Итак:

public void Post(string value)
{
}

и вызов из Fiddler:

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

Ответ 1

Поскольку у вас есть только один параметр, вы можете попытаться декорировать его с помощью [FromBody] или изменить метод так, чтобы он принимал DTO со значением в качестве свойства, как я предложил здесь: привязка параметра MVC4 RC WebApi

ОБНОВЛЕНИЕ: Официальный сайт ASP.NET был обновлен сегодня с отличным объяснением: https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part- 1

В двух словах, при отправке одного простого типа в теле, отправьте только значение с префиксом знака равенства (=), например, body:

=test

Ответ 2

Я сегодня почесываю голову над этим.

Мое решение - изменить [FromBody] на HttpRequestMessage, по существу перемещая стек HTTP.

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

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

My original endpoint

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

enter image description here

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

enter image description here

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

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

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

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

Ответ 3

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

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

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

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

Content-Type: application/json

Ответ 4

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

код webapi:

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

клиентский код:

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

Ответ 5

Я использовал Postman, и я делал ту же ошибку. Передача value как объекта json вместо строки

{
    "value": "test"
}

Очевидно, что предыдущий один неверен, когда параметр api имеет строку типа.

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

"test"

Ответ 6

Попробуйте создать класс для работы в качестве модели данных, а затем отправьте объект 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, content-type - 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;

Ответ 7

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

Единственный трюк: если аргумент метода 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-строку в тело сообщения без внешних двойных кавычек и избегаете внутренних кавычек, тогда она должна быть подвержена анализу классу модели (тип аргумента), например, {"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"
}

Ответ 8

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

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

Ответ 9

Это сработало для меня:

  • Создайте класс 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) {
            ...
        }
    });
    

Ответ 10

Для тех, у кого такая же проблема с Swagger или Postman, как у меня, если вы передаете простой атрибут в виде строки в сообщении, даже с указанным "ContentType", вы все равно получите нулевое значение.

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

MyValue

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

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

"MyValue"

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

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

Ответ 11

У меня была та же проблема, и я обнаружил, что при изменении типа содержимого на "application/json" эта проблема не устранена. Однако "application/json; charset = utf-8" работало.

Ответ 12

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

Ответ 13

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

Ответ 14

Добавление строки

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

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

Ответ 15

С Angular я смог передать данные в этом формате:

 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, иначе post '=' + JSON.stringify({..:...,..:...})

Ответ 16

Если вы используете 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" за подсказку.

Ответ 17

Если вы уверены в своем отправленном JSON, вы должны тщательно проследить свой API:

  • Установить пакет Microsoft.AspNet.WebApi.Tracing
  • Добавить config.EnableSystemDiagnosticsTracing(); в классе WebApiConfig внутри метода Register.

Теперь просмотрите вывод 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 manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Ответ 18

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

В моем случае сложный тип не был связан, но я не выполнял 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
    }
}

Ответ 19

У меня была такая же проблема в Fiddler. Я уже имел Content-Type: application/json; charset=utf-8 или Content-Type: application/json в заголовке запроса.

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

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

Ответ 20

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

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

Ответ 21

Лучшее решение для меня - полный 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;
}

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

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

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

Я сказал, исходя из этого URL:

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

Ответ 22

Для сложных типов Web API пытается прочитать значение из тела сообщения с использованием форматера медиа-типа.

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

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

Ответ 23

Я немного опаздываю на вечеринку, но любой, кто натыкается на значение NULL, переданное при использовании контроллера, просто добавляет "=" к фронту вашего запроса POST.

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

Ответ 24

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

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

С#:

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

Ответ 25

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

Если у вас есть json вот так:

{
    "shoeSize": 10.5
}

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

class Shoe{
    public int shoeSize;
}

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

Ответ 26

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

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

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

Ответ 27

Я довольно поздно к этому, но имел похожие проблемы, и после того, как я провел много ответов здесь и получил опыт, я нашел самое легкое/легкое решение для передачи одного или нескольких параметров в 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.

Сначала действие Controller Action, для этого решения также требуется библиотека 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 также позволяет динамически перебирать все переданные параметры, позволяя упростить масштабируемость, если ваши параметры будут меняться со временем. Надеюсь, это поможет кому-то, кто борется, как я.

Ответ 28

Правильный перенос одного параметра в теле на WebAPI работает с этим кодом $.post(url, { '': productId }

И поймать его в действии [HttpPost] public ShoppingCartAddRemoveViewModel Delete([FromBody]string value)

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

Ответ 29

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

Есть 2 решения вашей проблемы.

  • Создайте простой класс со свойством " значение", а затем используйте этот параметр как параметр, и в этом случае привязка модели Web API будет считывать объект JSON из запроса и связывать его с вашим объектом param Свойство "values".

  • Просто передайте строковое значение test ", и оно будет работать.

Ответ 30

Если вы помещаете аннотацию [FromBody], и у вас есть объект Dto в качестве параметра вашего метода и до сих пор не можете получить данные, начните изучать свойства и поля вашего DTO.

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