Як передавати дані json POST методу Web API як об’єкт?


304

Програма ASP.NET MVC4 Web API визначає метод публікації для збереження клієнта. Клієнт передається у форматі json в тіло запиту 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

EDIT : 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
переконайтесь, що під час використання fiddler для типу вмісту написано "Content-Type: application / json". Ура!
ioWint

1
Ви просто вирішили мені день роботи !!! Ця крихітна функція "JSON.stringify (data)" зробила це!
Гіл Аллен

1
Майте на увазі, що якщо ви це зробите (зміните заголовок типу вмісту) і ви робите запит CORS, jQuery почне додавати запити OPTIONS перед політом перед вашим POST, з яким повинен працювати сервер.
Арбітр

1
Через проблему зі складними типами я думаю, що це звичка просто вказувати 'contentType:' application / json; ' і json впорядкує об'єкт js, і тоді не потрібно використовувати атрибут [FromBody].
BornToCode

69

Робота з POST в webapi може бути складною! Хочеться додати до вже правильної відповіді ..

Зосереджуватиметься конкретно на POST, оскільки робота з GET є дрібницею. Я не думаю, що багато хто шукатиме проблеми для вирішення проблеми з GET за допомогою webapis. Все одно ..

Якщо у вас запитання: - У MVC Web Api, як до - Використовувати власні імена методу дії, крім загальних дієслів HTTP? - Виконувати кілька постів? - Опублікувати кілька простих типів? - Опублікувати складні типи через jQuery?

Тоді можуть допомогти наступні рішення:

По- перше, використовувати призначені для користувача методи дій в 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). Це було те, що я змусив мою голову про відпочиваючих клієнтів (як, наприклад, розширення клієнта 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, звичайно, $ .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 тепер ми можемо використовувати декоратори маршрутів. Тож це питання насамперед вирішене. asp.net/web-api/overview/web-api-routing-and-action/…
Vaibhav

2
Хочеться додати спостереження. Іноді причиною збою прив'язки моделі (null) на стороні 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, вони мають автоматично створене резервне поле, яке робить їх властивостями.
paqogomez

Це так правда і дивно. Звичайні класи лише з полями не прив'язуватимуться до публікацій, але властивості будуть. BTW: Досі не пояснює, чому це так ...? Я можу лише здогадуватися, що внутрішня логіка прив'язуватиме лише дані JSON до полів та формуватиме дані публікації до властивостей, і це просто так ...?
Джеймс Вілкінс

1
Це так, тому що код шукає лише властивості. Оскільки використання загальнодоступних полів не є найкращою практикою, команда MS вирішила не допускати не найкращих сценаріїв практики, що є досить вагомою причиною IMHO.
Ерік Філіпс

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
тип даних не потрібен.
Ерік Філіпс

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

Корпорація Майкрософт подала хороший приклад цього:

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.