Làm cách nào để chuyển dữ liệu POST json sang phương thức API Web dưới dạng đối tượng?


304

Ứng dụng API ASP.NET MVC4 xác định phương thức bài để cứu khách hàng. Khách hàng được thông qua định dạng json trong cơ thể yêu cầu POST. Tham số khách hàng trong phương thức post chứa giá trị null cho các thuộc tính.

Làm thế nào để khắc phục điều này để dữ liệu được đăng sẽ được chuyển qua làm đối tượng khách hàng?

Nếu có thể Loại nội dung: application / x-www-form-urlencoding nên được sử dụng vì tôi không biết cách thay đổi nó trong phương thức javascript mà bài đăng mẫu.

Điều khiển:

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

Yêu cầu:

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

Câu trả lời:


525

EDIT : 31/10/2017

Mã / cách tiếp cận tương tự cũng sẽ hoạt động cho Asp.Net Core 2.0 . Sự khác biệt chính là, trong lõi asp.net, cả bộ điều khiển api web và bộ điều khiển Mvc được hợp nhất với nhau thành mô hình bộ điều khiển duy nhất. Vì vậy, kiểu trả về của bạn có thể là IActionResultmột trong những cách thực hiện (Ví dụ OkObjectResult:)


Sử dụng

contentType:"application/json"

Bạn cần sử dụng JSON.stringifyphương thức để chuyển đổi nó thành chuỗi JSON khi bạn gửi nó,

Và chất kết dính mô hình sẽ liên kết dữ liệu json với đối tượng lớp của bạn.

Các mã dưới đây sẽ hoạt động tốt (thử nghiệm)

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

Kết quả

nhập mô tả hình ảnh ở đây

contentTypethuộc tính cho máy chủ biết rằng chúng tôi đang gửi dữ liệu ở định dạng JSON. Vì chúng tôi đã gửi một cấu trúc dữ liệu JSON, liên kết mô hình sẽ diễn ra đúng.

Nếu bạn kiểm tra các tiêu đề của yêu cầu ajax, bạn có thể thấy rằng Content-Typegiá trị được đặt là application/json.

Nếu bạn không chỉ định rõ ràng ContentType, Nó sẽ sử dụng loại nội dung mặc định là application/x-www-form-urlencoded;


Chỉnh sửa vào tháng 11 năm 2015 để giải quyết các vấn đề khác có thể xảy ra trong các bình luận

Đăng một đối tượng phức tạp

Giả sử bạn có một lớp mô hình xem phức tạp như tham số phương thức hành động api web của bạn như thế này

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

và điểm cuối api web của bạn giống như

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

Tại thời điểm viết bài này, ASP.NET MVC 6 là phiên bản ổn định mới nhất và trong MVC6, cả hai bộ điều khiển api Web và bộ điều khiển MVC đều được kế thừa từ Microsoft.AspNet.Mvc.Controllerlớp cơ sở.

Để gửi dữ liệu đến phương thức từ phía máy khách, đoạn mã dưới đây sẽ hoạt động tốt

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

Mô hình ràng buộc hoạt động cho một số thuộc tính, nhưng không phải tất cả! Tại sao ?

Nếu bạn không trang trí tham số phương thức api web với [FromBody]thuộc tính

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

Và gửi mô hình (đối tượng javascript thô, không ở định dạng JSON) mà không chỉ định giá trị thuộc tính contentType

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

Liên kết mô hình sẽ hoạt động cho các thuộc tính phẳng trên mô hình, không phải các thuộc tính trong đó loại phức tạp / loại khác. Trong trường hợp của chúng tôi IdNamecác thuộc tính sẽ được liên kết chính xác với tham số m, nhưng thuộc Tagstính sẽ là một danh sách trống.

Vấn đề tương tự sẽ xảy ra nếu bạn đang sử dụng phiên bản ngắn, $.postsẽ sử dụng Kiểu nội dung mặc định khi gửi yêu cầu.

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

4
Không chắc chắn những gì tôi đã làm, nhưng tôi đã trở lại vào sáng nay và trở lại trong cùng một chiếc thuyền. Đối tượng là null trong bộ điều khiển. chúng ta lại đi lol
Grayson

1
đảm bảo loại nội dung được viết "Loại nội dung: ứng dụng / json" trong khi bạn đang sử dụng fiddler. Chúc mừng!
ioWint

1
Bạn chỉ đơn giản là giải quyết cho tôi một ngày làm việc !!! Hàm nhỏ này "JSON.opesify (data)" đã tạo ra nó!
Gil Allen

1
Hãy ghi nhớ rằng nếu bạn thực hiện việc này (thay đổi tiêu đề Kiểu nội dung) và bạn đang thực hiện một yêu cầu CORS, jQuery sẽ bắt đầu thêm các yêu cầu TÙY CHỌN trước khi POST mà máy chủ sẽ phải xử lý.
Trọng tài

1
Do vấn đề với các loại phức tạp, tôi nghĩ rằng đó là thói quen chỉ định cụ thể 'contentType:' application / json; ' và json xâu chuỗi đối tượng js và sau đó không cần sử dụng thuộc tính [FromBody].
Sinh ra ToCode

69

Làm việc với POST trong webapi có thể khó khăn! Muốn thêm vào câu trả lời đã đúng ..

Sẽ tập trung cụ thể vào POST vì giao dịch với GET là không đáng kể. Tôi không nghĩ nhiều người sẽ tìm kiếm xung quanh để giải quyết vấn đề với GET với webapis. Dù sao ..

Nếu câu hỏi của bạn là - Trong MVC Web Api, làm thế nào để - - Sử dụng tên phương thức hành động tùy chỉnh khác với các động từ HTTP chung? - Thực hiện nhiều bài? - Đăng nhiều loại đơn giản? - Đăng các loại phức tạp qua jQuery?

Sau đó, các giải pháp sau đây có thể giúp:

Trước tiên, để sử dụng Phương thức hành động tùy chỉnh trong API Web, hãy thêm một tuyến api web như:

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

Và sau đó bạn có thể tạo các phương thức hành động như:

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

Bây giờ, kích hoạt jQuery sau từ bảng điều khiển trình duyệt của bạn

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

Thứ hai, để thực hiện nhiều bài viết , thật đơn giản, tạo nhiều phương thức hành động và trang trí với attrib [HttpPost]. Sử dụng [ActionName ("MyAction")] để gán tên tùy chỉnh, v.v. Sẽ đến jQuery ở điểm thứ tư bên dưới

Thứ ba, Trước hết, việc đăng nhiều loại SIMPLE trong một hành động là không thể. Hơn nữa, có một định dạng đặc biệt để đăng ngay cả một loại đơn giản (ngoài việc truyền tham số trong chuỗi truy vấn hoặc kiểu REST). Đây là điểm khiến tôi đập đầu với các Khách hàng Nghỉ ngơi (như tiện ích mở rộng ứng dụng REST nâng cao của Fiddler và Chrome) và tìm kiếm trên web trong gần 5 giờ khi cuối cùng, URL sau đây được chứng minh là có ích. Sẽ trích dẫn nội dung liên quan cho liên kết có thể chết!

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: Nhận thấy cú pháp đặc biệt ?

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

Dù sao, chúng ta hãy vượt qua câu chuyện đó. Tiếp tục:

Thứ tư, đăng các loại phức tạp thông qua jQuery, ofcference, $ .ajax () sẽ nhanh chóng xuất hiện trong vai trò:

Giả sử phương thức hành động chấp nhận một đối tượng Person có id và tên. Vì vậy, từ 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) }
});

Và hành động sẽ như sau:

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

Tất cả những điều trên, làm việc cho tôi !! Chúc mừng!


2
Tôi dường như gặp phải vấn đề này cứ sau vài tháng, hầu hết thời gian cuối cùng tôi cũng giải quyết được, nhưng lần này tôi đã từ bỏ. Không có lời khuyên nào ở trên giải quyết nó cho tôi, vì vậy tôi quyết định biến nó thành một cách tiếp cận. Nếu nó quá khó để làm đúng, tại sao phải bận tâm? Dù sao nó cũng chỉ là một sự tiện lợi - chỉ cần lấy nội dung thành một chuỗi và sử dụng newtonsoft để chuyển đổi nó. Làm xong. Có lẽ mất 30 giây để giải quyết nó theo cách "khó" sau khi cố gắng khoảng một giờ để giải quyết nó theo cách "dễ dàng". Tôi không hoang dã về cách tiếp cận, nhưng có một vấn đề cơ bản với nó?
Kinetic

PS: Trong WebApi2, giờ đây chúng ta có thể sử dụng Route Decorators. Vì vậy, vấn đề này là chủ yếu, giải quyết. asp.net/web-api/overview/web-api-routing-and-ilities/iêu
Vaibhav

2
Muốn thêm một quan sát. Đôi khi, lý do khiến liên kết mô hình không thành công (null) ở phía WebAPI, khi chuyển một loại phức tạp (ví dụ: DTO), là một hoặc nhiều thuộc tính trong mô hình sẽ không tương thích (hoặc không phân tích cú pháp). Ví dụ. Một thuộc tính Guid được gán GUID không hợp lệ. Trong trường hợp này, hãy thử sử dụng các giá trị mặc định / trống cho tất cả các thuộc tính đối tượng và thử lại.
Vaibhav

10

Tôi vừa mới chơi với nó và phát hiện ra một kết quả khá kỳ lạ. Giả sử bạn có các thuộc tính công khai trên lớp của mình trong C # như thế này:

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

sau đó, bạn phải thực hiện thủ thuật JSON.opesify theo đề xuất của Shyju và gọi nó như thế này:

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

Tuy nhiên, nếu bạn định nghĩa getters và setters trên lớp của bạn như thế này:

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

sau đó bạn có thể gọi nó đơn giản hơn nhiều:

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

Cái này sử dụng tiêu đề HTTP:

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

Tôi không chắc chắn những gì đang xảy ra ở đây nhưng có vẻ như là một lỗi (tính năng?) Trong khung. Có lẽ các phương thức liên kết khác nhau đang gọi các "bộ điều hợp" khác nhau và trong khi bộ điều hợp cho ứng dụng / json hoạt động với các thuộc tính công khai, thì phương thức cho dữ liệu được mã hóa biểu mẫu lại không.

Tôi không có ý tưởng nào sẽ được coi là thực hành tốt nhất mặc dù.


6
Thuộc tính vs các trường là tại sao nó khác nhau. Tài sản là thực hành tốt nhất. Những gì bạn gọi thuộc tính trong ví dụ đầu tiên trên thực tế là các trường. Khi bạn đặt get / set cho chúng, sau đó chúng có trường sao lưu được tạo tự động làm cho chúng thuộc tính.
paqogomez

Điều này rất đúng và kỳ quặc. Các lớp bình thường chỉ có các trường sẽ không liên kết để tạo thành các bài đăng, nhưng các thuộc tính sẽ. BTW: Vẫn không giải thích tại sao đây là trường hợp ...? Tôi chỉ có thể đoán logic bên trong sẽ chỉ liên kết dữ liệu JSON với các trường và tạo dữ liệu đăng lên các thuộc tính và đó chỉ đơn giản là nó ...?
James Wilkins

1
Đó là trường hợp bởi vì mã chỉ tìm các thuộc tính. Vì sử dụng các lĩnh vực công cộng không phải là thực tiễn tốt nhất, nhóm MS đã quyết định không cho phép các tình huống không thực hành tốt nhất, lý do khá chính đáng IMHO.
Erik Philips

1

Sử dụng JSON.opesify () để lấy chuỗi ở định dạng JSON, đảm bảo rằng trong khi thực hiện cuộc gọi AJAX, bạn chuyển qua các thuộc tính được đề cập bên dưới:

  • contentType: 'application / json'

Dưới đây là mã jquery để thực hiện cuộc gọi bài ajax đến ap.net web 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 là không bắt buộc.
Erik Philips

0

Đảm bảo rằng dịch vụ WebAPI của bạn đang mong đợi một đối tượng được gõ mạnh với cấu trúc khớp với JSON mà bạn đang vượt qua. Và hãy chắc chắn rằng bạn xâu chuỗi JSON mà bạn đang POST.

Đây là JavaScript của tôi (sử dụng 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 ()
        {
        });

Và đây là Trình điều khiển WebAPI của tôi:

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

0

Theo mã để trả về dữ liệu ở định dạng json, thay vì xml -Web API 2: -

Đặt dòng sau vào tệp 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 đã đưa ra một ví dụ tốt về việc này:

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

Đầu tiên xác nhận yêu cầu

if (ModelState.IsValid)

và hơn là sử dụng dữ liệu nối tiếp.

Content = new StringContent(update.Status)

Ở đây 'Trạng thái' là một trường thuộc loại phức tạp. Việc nối tiếp được thực hiện bởi .NET, không cần phải lo lắng về điều đó.


0

1) Ở phía khách hàng của bạn, bạn có thể gửi cho bạn yêu cầu http.post theo chuỗi như bên dưới

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

2) Sau đó, trong trình điều khiển api web của bạn, bạn có thể giải tuần tự hóa nó

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

3) Lớp ApiReceuredListOfObjects của bạn sẽ giống như bên dưới

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

    }

4) đảm bảo rằng chuỗi được tuần tự hóa của bạn (IndexInfo ở đây) trở nên giống như cấu trúc bên dưới trước lệnh JsonConvert.DeserializeObject trong bước 2

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.