WebAPI หลายพารามิเตอร์ Put / Post


154

ฉันกำลังพยายามโพสต์หลายพารามิเตอร์ในตัวควบคุม WebAPI หนึ่งพารามิเตอร์มาจาก URL และอีกอันจากเนื้อหา นี่คือ URL: /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

นี่คือรหัสควบคุมของฉัน:

public HttpResponseMessage Put(Guid offerId, OfferPriceParameters offerPriceParameters)
{
    //What!?
    var ser = new DataContractJsonSerializer(typeof(OfferPriceParameters));
    HttpContext.Current.Request.InputStream.Position = 0;
    var what = ser.ReadObject(HttpContext.Current.Request.InputStream);

    return new HttpResponseMessage(HttpStatusCode.Created);
}

เนื้อหาของร่างกายอยู่ใน JSON:

{
    "Associations":
    {
        "list": [
        {
            "FromEntityId":"276774bb-9bd9-4bbd-a7e7-6ed3d69f196f",
            "ToEntityId":"ed0d2616-f707-446b-9e40-b77b94fb7d2b",
            "Types":
            {
                "list":[
                {
                    "BillingCommitment":5,
                    "BillingCycle":5,
                    "Prices":
                    {
                        "list":[
                        {
                            "CurrencyId":"274d24c9-7d0b-40ea-a936-e800d74ead53",
                            "RecurringFee":4,
                            "SetupFee":5
                        }]
                    }
                }]
            }
        }]
    }
}

ความคิดใด ๆ ที่ว่าทำไมการผูกพันเริ่มต้นไม่สามารถผูกกับofferPriceParametersอาร์กิวเมนต์ของตัวควบคุมของฉันได้? มันถูกตั้งค่าเป็นศูนย์เสมอ DataContractJsonSerializerแต่ผมสามารถกู้คืนข้อมูลจากร่างกายโดยใช้

ฉันพยายามใช้FromBodyคุณลักษณะของอาร์กิวเมนต์ แต่ก็ใช้ไม่ได้เช่นกัน

คำตอบ:


78
[HttpPost]
public string MyMethod([FromBody]JObject data)
{
    Customer customer = data["customerData"].ToObject<Customer>();
    Product product = data["productData"].ToObject<Product>();
    Employee employee = data["employeeData"].ToObject<Employee>();
    //... other class....
}

ใช้อ้างอิง

using Newtonsoft.Json.Linq;

ใช้คำขอ JQuery Ajax

var customer = {
    "Name": "jhon",
    "Id": 1,
};
var product = {
    "Name": "table",
    "CategoryId": 5,
    "Count": 100
};
var employee = {
    "Name": "Fatih",
    "Id": 4,
};

var myData = {};
myData.customerData = customer;
myData.productData = product;
myData.employeeData = employee;

$.ajax({
    type: 'POST',
    async: true,
    dataType: "json",
    url: "Your Url",
    data: myData,
    success: function (data) {
        console.log("Response Data ↓");
        console.log(data);
    },
    error: function (err) {
        console.log(err);
    }
});

3
ทางออกที่ดี หากยังไม่ชัดเจนสำหรับผู้อื่นคุณสามารถใช้. ToObject <int> (), .ToObject <decimal> (), .ToString () ฯลฯ หากคุณผ่านพารามิเตอร์ ajax แบบง่าย ๆ
secretwep

ขอบคุณฉันได้ลองวิธีแก้ปัญหาของคุณด้วยการสร้าง API ของตัวเองและทดสอบผ่าน Postman และทำงานได้ดี แต่ฉันได้เพิ่มพารามิเตอร์ตัวที่สี่เช่น var test = {"ชื่อ": "ทดสอบ"} และเพิ่มลงในวัตถุ myData และมันถูกส่งไปเรียบร้อยแล้ว มีอยู่แล้วเพื่อหลีกเลี่ยงปัญหานี้และ จำกัด เฉพาะวัตถุต้นฉบับ
Mlle116

@ H.Al ไม่ Newtonsoft.Json สามารถมีข้อมูล json ทุกชนิดที่ห้องสมุดรู้เกี่ยวกับการแปล คุณไม่สามารถป้องกันการส่งข้อมูล ขึ้นอยู่กับคุณที่จะใช้ข้อมูลที่เข้ามา
Fatih GÜRDAL

63

WebAPI ไม่สนับสนุนการเชื่อมโยงพารามิเตอร์ POST หลายตัว ดังที่โคลินชี้ให้เห็นมีข้อ จำกัด มากมายที่ระบุไว้ในโพสต์บล็อกของฉันที่เขาอ้างถึง

มีวิธีแก้ปัญหาโดยการสร้าง binder พารามิเตอร์ที่กำหนดเอง รหัสการทำเช่นนี้น่าเกลียดและซับซ้อน แต่ฉันโพสต์โค้ดพร้อมคำอธิบายอย่างละเอียดในบล็อกของฉันพร้อมที่จะเสียบเข้ากับโครงการที่นี่:

การส่งค่า POST แบบง่าย ๆ ไปยัง ASP.NET Web API


1
เครดิตทั้งหมดไปถึงคุณ :) ฉันเพิ่งจะอ่านซีรี่ส์ของคุณบน WebAPI ในขณะที่เริ่มใช้งานของฉันเองเมื่อคำถามนี้โผล่ขึ้นมา
โคลินยังก์

ขอบคุณ! มีประโยชน์มาก
Normand Bedard

2
ตั้งแต่ปี 2019 เริ่มต้นแล้ว
สูงสุด

@John - มีเวอร์ชันพื้นฐานที่รองรับฟังก์ชันการทำงานนี้หรือไม่ ไม่มีความสำเร็จใด ๆ ในวันนี้
Neil Moss

26

หากใช้การกำหนดเส้นทางของแอตทริบิวต์คุณสามารถใช้แอตทริบิวต์ [FromUri] และ [FromBody]

ตัวอย่าง:

[HttpPost()]
[Route("api/products/{id:int}")]
public HttpResponseMessage AddProduct([FromUri()] int id,  [FromBody()] Product product)
{
  // Add product
}

1
ฉันใช้วิธีเดียวกันทุกประการ ฉันต้องส่งนางแบบสองคนไปสู่การปฏิบัติ ฉันผ่านไปหนึ่งคุณสมบัติที่น้อยกว่าผ่านสตริงการสืบค้นและอื่น ๆ จากเนื้อหา นอกจากนี้คุณไม่จำเป็นต้องระบุ [FromBody] attribyte
Sergey G.

1
ฉันไม่สามารถใช้งานได้คุณมีตัวอย่างที่สมบูรณ์มากขึ้นหรือไม่
The One

ฉันไม่คิดว่านี่เป็นวิธีที่ถูกต้องในการส่งข้อมูลผ่านวิธี POST แต่ฉันไม่เห็นวิธีแก้ไขปัญหาอื่นหากคุณต้องส่งรุ่น 2 แบบทางไปรษณีย์
Alexandr

คำตอบนี้คือ Jam!
Leonardo Wildt

1
ฉันใช้ aspnetcore และคุณต้องใช้[FromRoute]แทน[FromUri]
DanielV

19

เราผ่านวัตถุ Json โดยวิธี HttpPost และแยกวิเคราะห์เป็นวัตถุแบบไดนามิก มันใช้งานได้ดี นี่คือรหัสตัวอย่าง:

WebAPI:

[HttpPost]
public string DoJson2(dynamic data)
{
   //whole:
   var c = JsonConvert.DeserializeObject<YourObjectTypeHere>(data.ToString()); 

   //or 
   var c1 = JsonConvert.DeserializeObject< ComplexObject1 >(data.c1.ToString());

   var c2 = JsonConvert.DeserializeObject< ComplexObject2 >(data.c2.ToString());

   string appName = data.AppName;
   int appInstanceID = data.AppInstanceID;
   string processGUID = data.ProcessGUID;
   int userID = data.UserID;
   string userName = data.UserName;
   var performer = JsonConvert.DeserializeObject< NextActivityPerformers >(data.NextActivityPerformers.ToString());

   ...
}

ประเภทของวัตถุที่ซับซ้อนอาจเป็นวัตถุอาร์เรย์และพจนานุกรม

ajaxPost:
...
Content-Type: application/json,
data: {"AppName":"SamplePrice",
       "AppInstanceID":"100",
       "ProcessGUID":"072af8c3-482a-4b1c‌​-890b-685ce2fcc75d",
       "UserID":"20",
       "UserName":"Jack",
       "NextActivityPerformers":{
           "39‌​c71004-d822-4c15-9ff2-94ca1068d745":[{
                 "UserID":10,
                 "UserName":"Smith"
           }]
       }}
...

1
เราสามารถใส่พารามิเตอร์หลายรูปแบบเป็นวัตถุ json เดียวเพื่อโพสต์และเราจะแยกมันไปยังวัตถุหลายวัตถุในภายหลังในฝั่งเซิร์ฟเวอร์ นี่อาจเป็นวิธีคิดอีกวิธีหนึ่ง
Bes Ley

@EkoosticMartin ทำงานได้ดีคุณต้องแยกวิเคราะห์ชนิดไดนามิกโดยใช้: JsonConvert.DeserializeObject <YourObjectTypeHere> (data.ToString ()); ตัวอย่างเนื้อหาข้อมูลที่ซับซ้อนอยู่ที่นี่ซึ่งรวมถึงอาร์เรย์และวัตถุพจนานุกรม { "AppName": "SamplePrice", "AppInstanceID": "100", "ProcessGUID": "072af8c3-482a-4b1c-890b-685ce2fcc75d", "UserID": "20", "ชื่อผู้ใช้": "แจ็ค"," NextActivityPerformers ": {" 39c71004-d822-4c15-9ff2-94ca1068d745 ": [{" หมายเลขผู้ใช้ ": 10," ชื่อผู้ใช้ ":" สมิ ธ "}]}}
ย์เลย์

1
โอเคแน่ใจแล้วจากนั้นใช้พารามิเตอร์สตริงเดียวไม่มีความแตกต่าง
EkoostikMartin

Single doesnt แปลว่าง่ายสตริง json สามารถรวมกับวัตถุประเภทต่าง ๆ มากมาย นี่คือประเด็นสำคัญและเป็นอีกวิธีในการแก้ปัญหา
Bes Ley

1
ทางออกที่ดีเยี่ยม! ขอบคุณ :)
Carl R

10

คลาสพารามิเตอร์แบบง่ายสามารถใช้เพื่อส่งพารามิเตอร์หลายตัวในโพสต์:

public class AddCustomerArgs
{
    public string First { get; set; }
    public string Last { get; set; }
}

[HttpPost]
public IHttpActionResult AddCustomer(AddCustomerArgs args)
{
    //use args...
    return Ok();
}

โอกาสใด ๆ ที่คุณรู้ว่าคำขอ POST ตัวอย่างควรมีลักษณะอย่างไร
นาเดีย Solovyeva

@NadiaSolovyeva เป็นมากกว่าสตริงข้อความค้นหาเนื่องจากข้อมูลที่โพสต์อยู่ในเนื้อหาไม่ใช่ข้อความค้นหา ฉันชอบใช้ PostMan เพื่อสร้างคิวรีทดสอบจากนั้นคุณสามารถดูได้ว่ามันมีลักษณะอย่างไร
Greg Gum

ไม่เป็นไรฉันได้พบวิธีการทำแล้ว ส่วนหัว POST: เนื้อหาประเภท: application / json; เนื้อหาของ POST: {"First": "1", "Last": "1000"}
Nadia Solovyeva

9

คุณสามารถอนุญาตให้ใช้พารามิเตอร์ POST หลายรายการโดยใช้คลาส MultiPostParameterBinding จากhttps://github.com/keith5000/MultiPostParameterBinding

วิธีใช้:

1) ดาวน์โหลดรหัสในโฟลเดอร์ซอร์สและเพิ่มลงในโครงการ Web API ของคุณหรือโครงการอื่น ๆ ในโซลูชัน

2) ใช้แอททริบิวต์[MultiPostParameters]ในวิธีการทำงานที่ต้องการสนับสนุนพารามิเตอร์ POST หลายรายการ

[MultiPostParameters]
public string DoSomething(CustomType param1, CustomType param2, string param3) { ... }

3) เพิ่มบรรทัดนี้ใน Global.asax.cs ไปที่เมธอด Application_Start ที่ใดก็ได้ก่อนการเรียกไปยังGlobalConfiguration.Configure (WebApiConfig.Register) :

GlobalConfiguration.Configuration.ParameterBindingRules.Insert(0, MultiPostParameterBinding.CreateBindingForMarkedParameters);

4) ให้ลูกค้าของคุณผ่านพารามิเตอร์เป็นคุณสมบัติของวัตถุ ตัวอย่างวัตถุ JSON สำหรับDoSomething(param1, param2, param3)วิธีการคือ:

{ param1:{ Text:"" }, param2:{ Text:"" }, param3:"" }

ตัวอย่าง JQuery:

$.ajax({
    data: JSON.stringify({ param1:{ Text:"" }, param2:{ Text:"" }, param3:"" }),
    url: '/MyService/DoSomething',
    contentType: "application/json", method: "POST", processData: false
})
.success(function (result) { ... });

เยี่ยมชมลิงค์สำหรับรายละเอียดเพิ่มเติม

ข้อจำกัดความรับผิดชอบ: ฉันเกี่ยวข้องโดยตรงกับทรัพยากรที่เชื่อมโยง


7

คำถามและความคิดเห็นที่ดี - เรียนรู้มากจากคำตอบที่นี่ :)

เป็นตัวอย่างเพิ่มเติมโปรดทราบว่าคุณสามารถผสมเนื้อหาและเส้นทางเช่น

[RoutePrefix("api/test")]
public class MyProtectedController 
{
    [Authorize]
    [Route("id/{id}")]
    public IEnumerable<object> Post(String id, [FromBody] JObject data)
    {
        /*
          id                                      = "123"
          data.GetValue("username").ToString()    = "user1"
          data.GetValue("password").ToString()    = "pass1"
         */
    }
}

การโทรแบบนี้:

POST /api/test/id/123 HTTP/1.1
Host: localhost
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer x.y.z
Cache-Control: no-cache

username=user1&password=pass1


enter code here

ฉันต้องการส่งพารามิเตอร์ชนิดซับซ้อน 2 ตัว เช่นเดียวกับ [HttpPost] สตริงสาธารณะ UploadFile (UploadMediaFile mediaFile, ไบต์ [] ข้อมูล) วิธีการทำ
Başar Kaya

2

เส้นทางของคุณมีลักษณะอย่างไรในกรณีนี้

คุณโพสต์ URL นี้:

/offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

เพื่อให้การทำงานเป็นไปได้ฉันคาดหวังว่าจะมีการกำหนดเส้นทางเช่นนี้ในของคุณ WebApiConfig :

routeTemplate: {controller}/{offerId}/prices/

สมมติฐานอื่น ๆ คือ: - ตัวควบคุมของคุณถูกเรียก OffersControllerการควบคุมของคุณเรียกว่า - วัตถุ JSON ที่คุณส่งผ่านในคำขอเนื้อหาเป็นประเภทOfferPriceParameters(ไม่ใช่ประเภทที่ได้รับมา) - คุณไม่มีวิธีการอื่นใดในตัวควบคุมที่อาจรบกวนการทำงานนี้ (ถ้าคุณลองลองแสดงความคิดเห็นและดูว่าอะไรบ้าง เกิดขึ้น)

และดังที่ฟิลิปส์กล่าวถึงมันจะช่วยให้คำถามของคุณถ้าคุณเริ่มยอมรับคำตอบเพราะ 'ยอมรับอัตรา 0%' อาจทำให้ผู้คนคิดว่าพวกเขากำลังเสียเวลา


เส้นทางของฉันคือ "ข้อเสนอ / {offerId} / ราคา" นี่เป็นวิธีเดียวในตัวควบคุมของฉัน
Normand Bedard

2

หากคุณไม่ต้องการไปทาง ModelBinding คุณสามารถใช้ DTO เพื่อทำสิ่งนี้ให้คุณ ตัวอย่างเช่นสร้างการกระทำ POST ใน DataLayer ซึ่งยอมรับประเภทที่ซับซ้อนและส่งข้อมูลจาก BusinessLayer คุณสามารถทำได้ในกรณีที่มีการเรียกใช้ UI-> API

นี่คือตัวอย่าง DTO มอบหมายอาจารย์ให้กับนักเรียนและมอบหมายเอกสารหลายชุด / ให้กับนักเรียน

public class StudentCurriculumDTO
 {
     public StudentTeacherMapping StudentTeacherMapping { get; set; }
     public List<Paper> Paper { get; set; }
 }    
public class StudentTeacherMapping
 {
     public Guid StudentID { get; set; }
     public Guid TeacherId { get; set; }
 }

public class Paper
 {
     public Guid PaperID { get; set; }
     public string Status { get; set; }
 }

จากนั้นแอ็คชันใน DataLayer สามารถสร้างเป็น:

[HttpPost]
[ActionName("MyActionName")]
public async Task<IHttpActionResult> InternalName(StudentCurriculumDTO studentData)
  {
     //Do whatever.... insert the data if nothing else!
  }

วิธีเรียกจาก BusinessLayer:

using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", dataof_StudentCurriculumDTO)
  {
     //Do whatever.... get response if nothing else!
  }

ตอนนี้จะยังคงทำงานได้ถ้าฉันต้องการส่งข้อมูลของนักเรียนหลายคนพร้อมกัน ปรับเปลี่ยนMyActionเช่นด้านล่าง ไม่จำเป็นต้องเขียน [FromBody], WebAPI2 ใช้ชนิดที่ซับซ้อน [FromBody] โดยค่าเริ่มต้น

public async Task<IHttpActionResult> InternalName(List<StudentCurriculumDTO> studentData)

จากนั้นในขณะที่เรียกมันให้ส่งList<StudentCurriculumDTO>ข้อมูล

using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", List<dataof_StudentCurriculumDTO>)

0

ขอพารามิเตอร์เช่น

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

Web api Code เป็นเช่นไร

public class OrderItemDetailsViewModel
{
    public Order order { get; set; }
    public ItemDetails[] itemDetails { get; set; }
}

public IHttpActionResult Post(OrderItemDetailsViewModel orderInfo)
{
    Order ord = orderInfo.order;
    var ordDetails = orderInfo.itemDetails;
    return Ok();
}

0

คุณสามารถรับ formdata เป็นสตริง:

    protected NameValueCollection GetFormData()
    {
        string root = HttpContext.Current.Server.MapPath("~/App_Data");
        var provider = new MultipartFormDataStreamProvider(root);

        Request.Content.ReadAsMultipartAsync(provider);

        return provider.FormData;
    }

    [HttpPost]
    public void test() 
    {
        var formData = GetFormData();
        var userId = formData["userId"];

        // todo json stuff
    }

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.