Как передать данные JSON POST в метод Web API как объект?


304

Приложение ASP.NET MVC4 Web API определяет метод публикации для сохранения клиента. Клиент передается в формате json в теле запроса POST. Параметр customer в методе post содержит нулевые значения для свойств.

Как это исправить, чтобы опубликованные данные передавались как объект клиента?

Если возможно, следует использовать Content-Type: application / x-www-form-urlencoded, так как я не знаю, как изменить его в методе javascript, который формирует сообщения.

контроллер:

public class CustomersController : ApiController {

  public object Post([FromBody] Customer customer)
        {
            return Request.CreateResponse(HttpStatusCode.OK,
            new
            {
                customer = customer
            });
        }
    }
}

public class Customer
    {
        public string company_name { get; set; }
        public string contact_name { get; set; }
     }

Запрос:

POST http://localhost:52216/api/customers HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

{"contact_name":"sdfsd","company_name":"ssssd"}

Ответы:


525

РЕДАКТИРОВАТЬ : 31/10/2017

Тот же код / ​​подход будет работать и для Asp.Net Core 2.0 . Основное отличие состоит в том, что в ядре asp.net контроллеры веб-API и контроллеры Mvc объединены в одну модель контроллера. Таким образом , ваш тип возвращаемого значения может быть IActionResultили один из его реализации (Ex: OkObjectResult)


использование

contentType:"application/json"

Вам нужно использовать JSON.stringifyметод для преобразования его в строку JSON при отправке,

И связыватель модели свяжет данные json с вашим объектом класса.

Приведенный ниже код будет работать нормально (проверено)

$(function () {
    var customer = {contact_name :"Scott",company_name:"HP"};
    $.ajax({
        type: "POST",
        data :JSON.stringify(customer),
        url: "api/Customer",
        contentType: "application/json"
    });
});

результат

введите описание изображения здесь

contentTypeсвойство сообщает серверу, что мы отправляем данные в формате JSON. Поскольку мы отправили структуру данных JSON, привязка к модели произойдет правильно.

Если вы проверите заголовки ajax-запроса, вы увидите, что Content-Typeзначение установлено как application/json.

Если вы не укажете contentType явно, он будет использовать тип контента по умолчанию, который application/x-www-form-urlencoded;


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

Проводка сложного объекта

Допустим, у вас есть класс модели сложного представления в качестве параметра метода действия веб-API, подобного этому

public class CreateUserViewModel
{
   public int Id {set;get;}
   public string Name {set;get;}  
   public List<TagViewModel> Tags {set;get;}
}
public class TagViewModel
{
  public int Id {set;get;}
  public string Code {set;get;}
}

и ваша конечная точка веб-API как

public class ProductController : Controller
{
    [HttpPost]
    public CreateUserViewModel Save([FromBody] CreateUserViewModel m)
    {
        // I am just returning the posted model as it is. 
        // You may do other stuff and return different response.
        // Ex : missileService.LaunchMissile(m);
        return m;
    }
}

На момент написания этой статьи ASP.NET MVC 6 является последней стабильной версией, а в MVC6 и контроллеры Web API, и контроллеры MVC наследуются от Microsoft.AspNet.Mvc.Controllerбазового класса.

Для отправки данных в метод со стороны клиента приведенный ниже код должен работать нормально

//Build an object which matches the structure of our view model class
var model = {
    Name: "Shyju",
    Id: 123,
    Tags: [{ Id: 12, Code: "C" }, { Id: 33, Code: "Swift" }]
};

$.ajax({
    type: "POST",
    data: JSON.stringify(model),
    url: "../product/save",
    contentType: "application/json"
}).done(function(res) {       
    console.log('res', res);
    // Do something with the result :)
});

Привязка моделей работает для некоторых свойств, но не для всех! Зачем ?

Если вы не украшаете параметр метода web api [FromBody]атрибутом

[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
    return m;
}

И отправьте модель (необработанный объект javascript, не в формате JSON) без указания значения свойства contentType

$.ajax({
    type: "POST",
    data: model,
    url: "../product/save"
}).done(function (res) {
     console.log('res', res);
});

Привязка модели будет работать для плоских свойств модели, а не для свойств, где тип является сложным / другого типа. В нашем случае Idи Nameсвойства будут правильно привязаны к параметру m, но Tagsсвойство будет пустым списком.

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

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});

4
Не уверен, что я сделал, но я вернулся сегодня утром и вернулся в той же лодке. Объект является нулевым в контроллере. здесь мы идем снова LOL
Грейсон

1
убедитесь, что тип контента написан "Content-Type: application / json", когда вы используете fiddler. Ура!
ioWint

1
Вы просто решили мне день работы !!! Эта крошечная функция "JSON.stringify (data)" сделала это!
Гил Аллен

1
Имейте в виду, что если вы сделаете это (измените заголовок Content-Type) и сделаете запрос CORS, jQuery начнет добавлять предварительные запросы OPTIONS перед вашим POST, который должен будет обработать сервер.
Арбитр

1
Из-за проблемы со сложными типами я думаю, что есть привычка просто указывать 'contentType:' application / json; ' и json преобразует в строку объект js, и тогда нет необходимости использовать атрибут [FromBody].
BornToCode

69

Работать с POST в webapi может быть сложно! Хотелось бы добавить к уже правильному ответу ..

Особое внимание будет уделено POST, поскольку работа с GET тривиальна. Я не думаю, что многие будут искать решение GET с помощью webapis. В любом случае ..

Если ваш вопрос - В MVC Web Api, как - использовать имена методов пользовательских действий, отличные от общих HTTP-глаголов? - Выполнить несколько постов? - Опубликовать несколько простых типов? - Публиковать сложные типы через JQuery?

Тогда могут помочь следующие решения:

Во-первых, чтобы использовать пользовательские методы действий в веб-API, добавьте маршрут веб-API следующим образом:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
        name: "ActionApi",
        routeTemplate: "api/{controller}/{action}");
}

И тогда вы можете создавать методы действий, такие как:

[HttpPost]
public string TestMethod([FromBody]string value)
{
    return "Hello from http post web api controller: " + value;
}

Теперь запустите следующий jQuery из консоли браузера

$.ajax({
    type: 'POST',
    url: 'http://localhost:33649/api/TestApi/TestMethod',
    data: {'':'hello'},
    contentType: 'application/x-www-form-urlencoded',
    dataType: 'json',
    success: function(data){ console.log(data) }
});

Во-вторых, чтобы выполнить несколько публикаций , просто создайте несколько методов действий и украсьте их атрибутом [HttpPost]. Используйте [ActionName («MyAction»)], чтобы назначить пользовательские имена и т. Д. Придет к jQuery в четвертом пункте ниже

В-третьих, во-первых, размещение нескольких ПРОСТЫХ типов в одном действии невозможно. Более того, существует специальный формат для публикации даже одного простого типа (кроме передачи параметра в строке запроса или стиле REST). Это было то, что заставило меня ударить головой об остальных клиентах (таких как Fiddler и расширение для расширенного REST-клиента Chrome) и охотиться по сети в течение почти 5 часов, когда, в конце концов, следующий URL оказался полезным. Будет ли цитировать соответствующий контент для ссылки может стать мертвым!

Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"na@Turbo.Tina"}

PS: Заметили своеобразный синтаксис ?

http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+Post+to+my+Web+Api

В любом случае, давайте закончим эту историю. Двигаясь дальше:

В-четвертых, публикация сложных типов с помощью jQuery, ofcourse, $ .ajax () быстро вступит в роль:

Допустим, метод действия принимает объект Person, который имеет идентификатор и имя. Итак, из JavaScript:

var person = { PersonId:1, Name:"James" }
$.ajax({
    type: 'POST',
    url: 'http://mydomain/api/TestApi/TestMethod',
    data: JSON.stringify(person),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function(data){ console.log(data) }
});

И действие будет выглядеть так:

[HttpPost]
public string TestMethod(Person person)
{
    return "Hello from http post web api controller: " + person.Name;
}

Все вышеперечисленное сработало у меня !! Ура!


2
Кажется, я сталкиваюсь с этой проблемой каждые несколько месяцев, большую часть времени, в конце концов, решаю ее, но на этот раз я сдался. Ни один из приведенных выше советов не решил для меня, поэтому я решил использовать это как подход. Если это так сложно понять, зачем? В любом случае это только удобство - просто возьмите содержимое в виде строки и используйте newtonsoft для его преобразования. Готово. Потребовалось, вероятно, 30 секунд, чтобы решить его «трудным» путем, после попытки в течение часа решить его «легким» способом. Я не в восторге от такого подхода, но есть ли в этом фундаментальная проблема?
Кинетический

PS: в WebApi2 теперь мы можем использовать Route Decorators. Так что эта проблема в первую очередь решена. asp.net/web-api/overview/web-api-routing-and-actions/…
Вайбхав

2
Хотел бы добавить наблюдение. Иногда причина сбоя привязки модели (ноль) на стороне WebAPI при передаче сложного типа (например, DTO) заключается в том, что одно или несколько свойств в модели будут несовместимы (или не будут проанализированы). Например. Свойству Guid присваивается неверный GUID. В этом случае попробуйте использовать значения по умолчанию / пусто для всех свойств объекта и повторите попытку.
Вайбхав

10

Я просто играл с этим и обнаружил довольно странный результат. Скажем, у вас есть открытые свойства в вашем классе в C #, например:

public class Customer
{
    public string contact_name;
    public string company_name;
}

тогда вы должны выполнить трюк JSON.stringify, как предложено Shyju, и назвать его так:

var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
    type: "POST",
    data :JSON.stringify(customer),
    url: "api/Customer",
    contentType: "application/json"
});

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

public class Customer
{
    public string contact_name { get; set; }
    public string company_name { get; set; }
}

тогда вы можете назвать это намного проще:

$.ajax({
    type: "POST",
    data :customer,
    url: "api/Customer"
});

Это использует заголовок HTTP:

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

Я не совсем уверен, что здесь происходит, но это выглядит как ошибка (функция?) В рамках. Предположительно, разные методы привязки вызывают разные «адаптеры», и хотя адаптер для application / json один работает с открытыми свойствами, а другой для закодированных данных формы - нет.

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


6
Свойства против полей, поэтому его разные. Свойства - лучшая практика. То, что вы называете свойствами в этом первом примере, на самом деле являются полями. Когда вы помещаете их в get / set, у них появляется автоматически созданное вспомогательное поле, которое присваивает им свойства.
Пакогомес

Это так верно и странно. Обычные классы только с полями не будут связываться с постами, но свойства будут. Кстати: до сих пор не объясняет, почему это так ...? Я могу только догадываться, что внутренняя логика будет только связывать данные JSON с полями и формировать данные постов в свойствах, и это все просто ...?
Джеймс Уилкинс

1
Это так, потому что код ищет только свойства. С помощью открытых полей является не лучшая практика, команда MS решила не допускать не сценарии наилучшей практики, довольно хорошая причина , ИМХО.
Эрик Филипс

1

Используйте JSON.stringify (), чтобы получить строку в формате JSON, убедитесь, что при вызове AJAX вы передаете ниже указанные атрибуты:

  • contentType: 'application / json'

Ниже приведен код JQuery для выполнения пост-вызова ajax на веб-интерфейсе asp.net:

var product =
    JSON.stringify({
        productGroup: "Fablet",
        productId: 1,
        productName: "Lumia 1525 64 GB",
        sellingPrice: 700
    });

$.ajax({
    URL: 'http://localhost/api/Products',
    type: 'POST',
    contentType: 'application/json',
    data: product,
    success: function (data, status, xhr) {
        alert('Success!');
    },
    error: function (xhr, status, error) {
        alert('Update Error occurred - ' + error);
    }
});


2
dataType не требуется.
Эрик Филипс

0

Убедитесь, что ваша служба WebAPI ожидает строго типизированный объект со структурой, соответствующей JSON, который вы передаете. И убедитесь, что вы записываете JSON, который вы размещаете.

Вот мой JavaScript (с использованием AngluarJS):

$scope.updateUserActivity = function (_objuserActivity) {
        $http
        ({
            method: 'post',
            url: 'your url here',
            headers: { 'Content-Type': 'application/json'},
            data: JSON.stringify(_objuserActivity)
        })
        .then(function (response)
        {
            alert("success");
        })
        .catch(function (response)
        {
            alert("failure");
        })
        .finally(function ()
        {
        });

А вот и мой контроллер WebAPI:

[HttpPost]
[AcceptVerbs("POST")]
public string POSTMe([FromBody]Models.UserActivity _activity)
{
    return "hello";
}

0

Следующий код для возврата данных в формате json вместо xml -Web API 2: -

Поместите следующую строку в файл Global.asax

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

0
@model MVCClient.Models.ProductDetails

@{
    ViewBag.Title = "ProductDetails";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript">

    $(document).ready(function () {
        $("#Save").click(function () {
            var ProductDetails = new Object();
            ProductDetails.ProductName =  $("#txt_productName").val();
            ProductDetails.ProductDetail = $("#txt_desc").val();
            ProductDetails.Price= $("#txt_price").val();
            $.ajax({
                url: "http://localhost:24481/api/Product/addProduct",
                type: "Post",
                dataType:'JSON',
                data:ProductDetails, 

                success: function (data) {
                    alert('Updated Successfully');
                    //window.location.href = "../Index";
                },
                error: function (msg) { alert(msg); }
            });
        });
    });
    </script>
<h2>ProductDetails</h2>

<form id="form1" method="post">
    <fieldset>
        <legend>ProductDetails</legend>


        <div class="editor-label">
            @Html.LabelFor(model => model.ProductName)
        </div>
        <div class="editor-field">

            <input id="txt_productName" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ProductDetail)
        </div>
        <div class="editor-field">

            <input id="txt_desc" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductDetail)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">

            <input id="txt_price" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.Price)
        </div>



        <p>
            <input id="Save" type="button" value="Create" />
        </p>
    </fieldset>

</form>
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>

</form>



@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

0

Microsoft привела хороший пример этого:

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1

Сначала подтвердите запрос

if (ModelState.IsValid)

и чем использовать сериализованные данные.

Content = new StringContent(update.Status)

Здесь «Статус» - это поле в сложном типе. Сериализация выполняется .NET, об этом не нужно беспокоиться.


0

1) На стороне клиента вы можете отправить запрос http.post в виде строки, как показано ниже

var IndexInfo = JSON.stringify(this.scope.IndexTree);
this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType', "'" + IndexInfo + "'" ).then((response: any) => {}

2) Затем в вашем веб-контроллере API вы можете десериализовать его

public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
    {
var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}

3) Ваш класс ApiReceivedListOfObjects должен быть таким, как показано ниже

public class ApiReceivedListOfObjects<T>
    {
        public List<T> element { get; set; }

    }

4) убедитесь, что ваша сериализованная строка (здесь IndexInfo) становится похожей на структуру ниже, чем команда JsonConvert.DeserializeObject на шаге 2

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.