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

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

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

Если возможно, следует использовать 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"}
Решение

EDIT : 31/10/2017

Этот же код/подход будет работать и в Asp.Net Core 2.0. Основное отличие заключается в том, что в asp.net core контроллеры web api и Mvc объединены в единую модель контроллера. Поэтому возвращаемый тип может быть IActionResult или одной из его реализаций (например, OkObjectResult).


Использовать

contentType:"application/json"

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

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

Размещение сложного объекта

Допустим, у вас есть сложный класс модели представления в качестве параметра метода действия web api, например, так

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

а конечная точка web 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, которая при отправке запроса будет использовать стандартный Content-Type.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});
Комментарии (17)

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

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

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

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

Во-первых, чтобы использовать Custom Action Methods в Web API, добавьте web 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). Именно этот момент заставил меня биться головой с Rest-клиентами (такими как Fiddler и расширение Chrome'Advanced REST client) и охотиться в Интернете в течение почти 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, конечно же, оперативно задействует $.ajax():

Допустим, метод action принимает объект Person, у которого есть id и имя. Итак, из 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;
}

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

Комментарии (3)

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

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

Комментарии (4)

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

  • значение contentType: 'приложение/JSON'

Ниже даю код jQuery, чтобы сделать AJAX-вызов для веб asp.net API-интерфейс:

в

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

в

Комментарии (1)

Убедитесь, что ваш сервис вызова 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 ()
        {
        });

И вот мой веб-API контроллера:

[HttpPost]
[AcceptVerbs("POST")]
public string POSTMe([FromBody]Models.UserActivity _activity)
{
    return "hello";
}
Комментарии (0)

Следующий код, чтобы вернуть данные в формате JSON ,а не XML и веб-API 2 :-

Поместите следующую строку в мировой.файл 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>



        ProductDetails

        <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>



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



@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)

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

Комментарии (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)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects));}

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

public class ApiReceivedListOfObjects
    {
        public List 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""
        }
        ]
    }";
Комментарии (0)