จะส่งข้อมูล json POST ไปยังวิธี Web API เป็นวัตถุได้อย่างไร


304

แอปพลิเคชัน ASP.NET MVC4 Web API กำหนดวิธีการโพสต์เพื่อบันทึกลูกค้า ลูกค้าผ่านรูปแบบ json ในเนื้อความคำขอ POST พารามิเตอร์ลูกค้าในวิธีการโพสต์มีค่า Null สำหรับคุณสมบัติ

จะแก้ไขได้อย่างไรเพื่อที่ข้อมูลที่โพสต์จะถูกส่งเป็นวัตถุลูกค้า

ถ้าเป็นไปได้ 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"
    });
});

ผลลัพธ์

ป้อนคำอธิบายรูปภาพที่นี่

contentTypeproperty บอกเซิร์ฟเวอร์ว่าเรากำลังส่งข้อมูลในรูปแบบ JSON เนื่องจากเราส่งโครงสร้างข้อมูล JSON การเชื่อมโยงโมเดลจะเกิดขึ้นอย่างถูกต้อง

หากคุณตรวจสอบส่วนหัวคำขออาแจ็กซ์, คุณสามารถเห็นได้ว่าค่าถูกกำหนดให้เป็นContent-Typeapplication/json

หากคุณไม่ได้ระบุ contentType อย่างชัดเจนมันจะใช้ประเภทเนื้อหาเริ่มต้นซึ่งก็คือ application/x-www-form-urlencoded;


แก้ไขเมื่อพฤศจิกายน 2558 เพื่อแก้ไขปัญหาอื่น ๆ ที่อาจเกิดขึ้นในความคิดเห็น

การโพสต์วัตถุที่ซับซ้อน

สมมติว่าคุณมีคลาสโมเดลมุมมองที่ซับซ้อนเป็นพารามิเตอร์วิธีการดำเนินการทางเว็บ 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 ทั้งตัวควบคุมเว็บ 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;
}

และส่งโมเดล (อ็อบเจ็กต์ raw 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
ไม่แน่ใจว่าฉันทำอะไร แต่ฉันกลับมาในเช้านี้และกลับมาในเรือลำเดียวกัน วัตถุเป็นโมฆะในตัวควบคุม ที่นี่เราไปกันอีกแล้ว
เกรย์สัน

1
ตรวจสอบให้แน่ใจว่ามีการเขียนประเภทเนื้อหา "ประเภทเนื้อหา: แอปพลิเคชัน / json" ในขณะที่คุณใช้พู้ทำเล่น ไชโย!
ioWint

1
คุณเพียงแค่แก้ไขให้ฉันวันทำงาน !!! ฟังก์ชันเล็ก ๆ นี้ "JSON.stringify (data)" สร้างมันขึ้นมา!
Gil Allen

1
โปรดทราบว่าถ้าคุณทำเช่นนี้ (เปลี่ยนหัวข้อประเภทเนื้อหา) และคุณกำลังร้องขอ CORS jQuery จะเริ่มเพิ่มคำขอ preflight OPTIONS ก่อน POST ของคุณซึ่งเซิร์ฟเวอร์จะต้องจัดการ
ผู้ตัดสิน

1
เนื่องจากปัญหาเกี่ยวกับประเภทที่ซับซ้อนฉันคิดว่ามันเป็นนิสัยเพียง specifiy 'contentType:' application / json; ' และ json ทำให้วัตถุ js เป็นสตริงและจากนั้นไม่จำเป็นต้องใช้แอตทริบิวต์ [FromBody]
BornToCode

69

การทำงานกับ POST ใน webapi อาจเป็นเรื่องยาก! ต้องการเพิ่มคำตอบที่ถูกต้องแล้ว

จะเน้นเฉพาะใน POST เนื่องจากการรับมือกับ GET นั้นไม่สำคัญ ฉันไม่คิดว่าหลายคนจะค้นหาวิธีแก้ไขปัญหาด้วย GET ด้วย webapis อย่างไรก็ตาม..

หากคำถามของคุณคือ - ใน MVC Web Api, วิธีการ - ใช้ชื่อวิธีการกระทำที่กำหนดเองอื่นที่ไม่ใช่คำกริยา HTTP ทั่วไป? - ดำเนินการหลายโพสต์หรือไม่ - โพสต์หลายประเภทง่าย ๆ ? - โพสต์ประเภทที่ซับซ้อนผ่าน jQuery?

จากนั้นโซลูชันต่อไปนี้อาจช่วยได้:

ก่อนอื่นให้ใช้วิธีการกระทำที่กำหนดเองใน Web 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) }
});

ประการที่สองเพื่อทำการโพสต์หลายโพสต์มันง่ายสร้างวิธีการหลายอย่างและตกแต่งด้วย attrib [HttpPost] ใช้ [ActionName ("MyAction")] เพื่อกำหนดชื่อที่กำหนดเอง ฯลฯ จะมาที่ jQuery ในจุดที่สี่ด้านล่าง

ประการที่สามประการแรกการโพสต์SIMPLEหลายประเภทในการกระทำเดียวเป็นไปไม่ได้ นอกจากนี้ยังมีรูปแบบพิเศษในการโพสต์แม้แต่ประเภทที่เรียบง่ายเพียงอย่างเดียว (นอกเหนือจากการผ่านพารามิเตอร์ในสตริงการสืบค้นหรือสไตล์ 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 () จะเข้ามามีบทบาททันที:

ให้เราบอกว่าวิธีการดำเนินการรับวัตถุบุคคลที่มี 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;
}

จากทั้งหมดที่กล่าวมาเป็นประโยชน์สำหรับฉัน !! ไชโย!


2
ฉันดูเหมือนจะเจอปัญหานี้ทุกสองสามเดือนส่วนใหญ่ฉันจะแก้ปัญหาได้ในที่สุด แต่คราวนี้ฉันเลิกไปแล้ว ไม่มีเคล็ดลับข้างต้นแก้ให้ฉันดังนั้นฉันจึงตัดสินใจที่จะทิ้งสิ่งนี้ไว้เป็นแนวทาง ถ้ามันยากที่จะทำให้ถูกต้องทำไมต้องกังวล? มันเป็นเพียงความสะดวกสบายเท่านั้น - เพียงแค่นำเนื้อหามาเป็นสตริงและใช้ newtonsoft เพื่อแปลงมัน เสร็จสิ้น อาจใช้เวลา 30 วินาทีเพื่อแก้ปัญหา "ยาก" หลังจากลองใช้เวลาประมาณหนึ่งชั่วโมงเพื่อแก้ปัญหาด้วยวิธี "ง่าย" ฉันไม่ได้กลัวเกี่ยวกับวิธีการ แต่มีปัญหาพื้นฐานกับมันหรือไม่?
Kinetic

PS: ใน WebApi2 เราสามารถใช้ Route Decorators ได้แล้ว ดังนั้นปัญหานี้ได้รับการแก้ไขเป็นหลัก asp.net/web-api/overview/web-api-routing-and-actions/…
Vaibhav

2
ต้องการเพิ่มข้อสังเกต บางครั้งเหตุผลที่ทำให้การเชื่อมโยงโมเดลล้มเหลว (null) บนฝั่ง WebAPI เมื่อผ่านประเภทที่ซับซ้อน (เช่น DTO) นั่นคือคุณสมบัติอย่างน้อยหนึ่งอย่างในโมเดลจะไม่เข้ากัน (หรือไม่สามารถแยกวิเคราะห์) เช่น. Guid property ถูกกำหนด GUID ที่ไม่ถูกต้อง ในกรณีนี้ลองใช้ค่าเริ่มต้น / ว่างสำหรับคุณสมบัติของวัตถุทั้งหมดแล้วลองอีกครั้ง
Vaibhav

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

อย่างไรก็ตามหากคุณกำหนด getters และ setters ในชั้นเรียนของคุณเช่นนี้:

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

ฉันไม่แน่ใจว่าสิ่งที่เกิดขึ้นที่นี่ แต่ดูเหมือนว่าข้อผิดพลาด (คุณสมบัติ?) ในกรอบ สันนิษฐานว่าวิธีการเชื่อมโยงที่แตกต่างกันกำลังเรียก "อะแดปเตอร์" ที่แตกต่างกันและในขณะที่อะแดปเตอร์สำหรับแอปพลิเคชัน / json หนึ่งทำงานร่วมกับคุณสมบัติสาธารณะ

ฉันไม่มีความคิดที่จะถือว่าเป็นแนวปฏิบัติที่ดีที่สุด


6
คุณสมบัติเทียบกับเขตข้อมูลคือเหตุผลที่แตกต่างกัน คุณสมบัติเป็นแนวปฏิบัติที่ดีที่สุด สิ่งที่คุณเรียกคุณสมบัติในตัวอย่างแรกนั้นคืออันที่จริงแล้วฟิลด์ เมื่อคุณวาง get / set ไว้พวกมันจะมีเขตข้อมูลสำรองที่สร้างขึ้นอัตโนมัติซึ่งทำให้คุณสมบัตินั้น
paqogomez

นี่เป็นเรื่องจริงและแปลก คลาสปกติที่มีเฉพาะฟิลด์จะไม่ผูกกับโพสต์แบบฟอร์ม แต่คุณสมบัติจะ BTW: ยังไม่อธิบายว่าทำไมเป็นเช่นนี้ ... ? ฉันสามารถเดาได้ว่าตรรกะภายในจะผูกเฉพาะข้อมูล JSON กับเขตข้อมูลและสร้างข้อมูลการโพสต์ไปยังคุณสมบัติและนั่นเป็นเพียง ...
James Wilkins

1
อาจเป็นเพราะรหัสนั้นมองหาคุณสมบัติเท่านั้น เนื่องจากการใช้ฟิลด์สาธารณะไม่ใช่วิธีปฏิบัติที่ดีที่สุดทีม MS ตัดสินใจที่จะไม่อนุญาตสถานการณ์จำลองที่ไม่ดีที่สุดเหตุผลที่ IMHO ค่อนข้างดี
Erik Philips

1

ใช้JSON.stringify ()เพื่อรับสตริงในรูปแบบ JSON ตรวจสอบให้แน่ใจว่าในขณะที่ทำการโทร AJAX คุณจะส่งผ่านแอตทริบิวต์ด้านล่างที่กล่าวถึง:

  • contentType: 'application / json'

ด้านล่างเป็นรหัสการให้ jquery เพื่อทำการโทร ajax ไปยัง asp.net web 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);
    }
});


2
ไม่จำเป็นต้องใช้ dataType
Erik Philips

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 controller ของเว็บคุณสามารถทำการ deserialize ได้

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) ตรวจสอบให้แน่ใจว่าสตริงอนุกรมของคุณ (ดัชนีที่นี่) กลายเป็นโครงสร้างด้านล่างก่อนคำสั่ง JsonConvert.DeserializeObject ในขั้นตอนที่ 2

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.