WebAPI Nhiều thông số Đặt / Đăng


154

Tôi đang cố gắng đăng nhiều tham số trên bộ điều khiển WebAPI. Một param là từ URL, và cái kia từ cơ thể. Đây là url: /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

Đây là mã điều khiển của tôi:

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

Nội dung của phần thân là trong 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
                        }]
                    }
                }]
            }
        }]
    }
}

Bất cứ ý tưởng tại sao ràng buộc mặc định không thể liên kết với offerPriceParametersđối số của bộ điều khiển của tôi? Nó luôn được đặt thành null. Nhưng tôi có thể phục hồi dữ liệu từ cơ thể bằng cách sử dụng DataContractJsonSerializer.

Tôi cũng cố gắng sử dụng FromBodythuộc tính của đối số nhưng nó cũng không hoạt động.

Câu trả lời:


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

sử dụng giới thiệu

using Newtonsoft.Json.Linq;

Sử dụng Yêu cầu cho 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
Giải pháp tuyệt vời. Nếu nó chưa rõ ràng với người khác, bạn cũng có thể sử dụng .ToObject <int> (), .ToObject <binary> (), .ToString (), v.v. nếu bạn chuyển một cách đơn giản, nhiều tham số từ lệnh gọi ajax của bạn.
secretwep

Cảm ơn bạn, tôi đã thử giải pháp của bạn bằng cách tạo API của riêng tôi và thử nghiệm nó thông qua Postman và nó hoạt động tốt; Nhưng tôi đã thêm một tham số thứ tư như var test = {"Name": "test"} và thêm nó vào đối tượng myData và nó đã được gửi thành công; Có cách nào để tránh điều này và chỉ hạn chế các đối tượng ban đầu không?
Mlle116

@ H.Al Không, Newtonsoft.Json có thể có bất kỳ loại dữ liệu json nào mà thư viện biết về dịch thuật. Bạn không thể ngăn gửi dữ liệu. Tùy thuộc vào bạn sử dụng dữ liệu đến
Fatih GRDAL

63

Về cơ bản, WebAPI không hỗ trợ liên kết nhiều tham số POST. Như Colin chỉ ra rằng có một số hạn chế được nêu trong bài đăng trên blog của tôi mà anh ấy tham khảo.

Có một cách giải quyết bằng cách tạo ra một chất kết dính tham số tùy chỉnh. Mã để làm điều này thật xấu xí và hỗn độn, nhưng tôi đã đăng mã cùng với lời giải thích chi tiết trên blog của mình, sẵn sàng để cắm vào một dự án ở đây:

Truyền nhiều giá trị POST đơn giản cho API Web ASP.NET


1
Tất cả các khoản tín dụng dành cho bạn :) Tôi tình cờ đọc loạt bài của bạn trên WebAPI trong khi bắt đầu thực hiện của riêng tôi khi câu hỏi này xuất hiện.
Colin Young

Cảm ơn bạn! Rất hữu ích.
Normand Bedard

2
Tính đến năm 2019, bây giờ.
Tối đa

@ John - có phiên bản cơ sở mà chức năng này được hỗ trợ không? Không có bất kỳ thành công ngày hôm nay.
Neil Moss

26

Nếu định tuyến thuộc tính đang được sử dụng, bạn có thể sử dụng các thuộc tính [FromUri] và [FromBody].

Thí dụ:

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

1
Tôi đã sử dụng chính xác cùng một phương pháp. Tôi cần phải vượt qua hai mô hình để hành động. Tôi đã vượt qua một với các thuộc tính ít hơn thông qua chuỗi truy vấn và khác từ cơ thể. Ngoài ra, bạn không cần chỉ định rõ ràng biểu đồ [FromBody]
Sergey G.

1
Tôi không thể thực hiện công việc này, bạn có một ví dụ đầy đủ hơn không?
Một

Tôi không nghĩ rằng đây là cách đúng để gửi dữ liệu qua phương thức POST nhưng tôi không thấy giải pháp nào khác nếu bạn phải gửi 2 mô hình qua đường bưu điện.
Alexandr

Câu trả lời này là Jam!
Leonardo Wildt

1
Tôi đang sử dụng aspnetcore và bạn phải sử dụng [FromRoute]thay vì[FromUri]
DanielV

19

Chúng tôi đã truyền đối tượng Json bằng phương thức HttpPost và phân tích cú pháp trong đối tượng động. nó hoạt động tốt. đây là mã mẫu:

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

   ...
}

Loại đối tượng phức tạp có thể là đối tượng, mảng và từ điển.

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
Chúng ta có thể đặt nhiều tham số được định dạng dưới dạng một đối tượng json để đăng và chúng ta sẽ phân tích nó thành nhiều đối tượng sau này ở phía máy chủ. Đây có thể là một cách khác để suy nghĩ.
Bes Ley

@EkoosticMartin, Nó hoạt động tốt, bạn cần phân tích kiểu động bằng cách sử dụng: JsonConvert.DeserializeObject <YourObjectTypeHere> (data.ToString ()); Một mẫu nội dung dữ liệu phức tạp ở đây, nó bao gồm mảng và đối tượng từ điển. {"AppName": "Sampleprice", "AppInstanceID": "100", "ProcessGUID": "072af8c3-482a-4b1c-890b-685ce2fcc75d", "Tên người dùng": "20" NextActivityPerformers ": {" 39c71004-d822-4c15-9ff2-94ca1068d745 ": [{" UserID ": 10," Tên người dùng ":" Smith "}]}}
Bes Ley

1
Được rồi, chắc chắn, sau đó chỉ cần sử dụng một chuỗi param duy nhất, không có khác biệt.
EkoostikMartin

Single không có nghĩa là đơn giản, chuỗi json có thể được kết hợp với nhiều loại đối tượng khác nhau. Đây là điểm mấu chốt, và là một cách khác để giải quyết các câu hỏi.
Bes Ley

1
Giải pháp tuyệt vời! Cảm ơn :)
Carl R

10

Một lớp tham số đơn giản có thể được sử dụng để truyền nhiều tham số trong một bài:

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

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

Bất kỳ cơ hội nào bạn biết yêu cầu POST mẫu sẽ trông như thế nào?
Nadia Solovyeva

@NadiaSolovyeva, Nó không chỉ là một chuỗi truy vấn, vì thông tin POSTED nằm trong phần thân chứ không phải chuỗi truy vấn. Tôi thích sử dụng PostMan để thực hiện các truy vấn thử nghiệm, và sau đó bạn có thể thấy chính xác nó trông như thế nào.
Greg Gum

Không sao, tôi đã tìm ra cách để làm điều đó. Tiêu đề POST: Loại nội dung: application / json; Phần thân bài: {"Đầu tiên": "1", "Lần cuối": "1000"}
Nadia Solovyeva

9

Bạn có thể cho phép nhiều tham số POST bằng cách sử dụng lớp MultiPostParameterBinding từ https://github.com/keith5000/MultiPostParameterBinding

Để dùng nó:

1) Tải xuống mã trong thư mục Nguồn và thêm nó vào dự án API Web của bạn hoặc bất kỳ dự án nào khác trong giải pháp.

2) Sử dụng thuộc tính [MultiPostParameter] trên các phương thức hành động cần hỗ trợ nhiều tham số POST.

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

3) Thêm dòng này trong Global.asax.cs vào phương thức Application_Start ở bất cứ đâu trước khi gọi đến GlobalConfiguration.Configure (WebApiConfig.Register) :

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

4) Có khách hàng của bạn vượt qua các tham số như các thuộc tính của một đối tượng. Một đối tượng JSON ví dụ cho DoSomething(param1, param2, param3)phương thức là:

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

Ví dụ JQuery:

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

Truy cập liên kết để biết thêm chi tiết.

Tuyên bố miễn trừ trách nhiệm: Tôi liên kết trực tiếp với tài nguyên được liên kết.


7

Câu hỏi và ý kiến ​​hay - học được nhiều từ các câu trả lời ở đây :)

Như một ví dụ bổ sung, lưu ý rằng bạn cũng có thể kết hợp cơ thể và tuyến đường, vd

[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"
         */
    }
}

Gọi như thế này:

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

Tôi muốn gửi 2 tham số loại phức tạp. Giống như [httpPost] chuỗi công khai UploadFile (UploadMediaFile mediaFile, byte [] data) cách thực hiện.
Başar Kaya

2

RouteTemplate của bạn trông như thế nào cho trường hợp này?

Bạn đã đăng url này:

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

Để làm việc này, tôi mong đợi một định tuyến như thế này trong WebApiConfig:

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

Các giả định khác là: - bộ điều khiển của bạn được gọi OffersController. - đối tượng JSON mà bạn chuyển qua trong thân yêu cầu thuộc loại OfferPriceParameters(không phải bất kỳ loại dẫn xuất nào) - bạn không có bất kỳ phương thức nào khác trên bộ điều khiển có thể can thiệp vào điều này (nếu bạn làm vậy, hãy thử nhận xét chúng và xem cái gì xảy ra)

Và như Filip đã đề cập, nó sẽ giúp ích cho câu hỏi của bạn nếu bạn bắt đầu chấp nhận một số câu trả lời vì 'tỷ lệ chấp nhận 0%' có thể khiến mọi người nghĩ rằng họ đang lãng phí thời gian


Tuyến đường của tôi là "Offer / {OfferId} / price". Đây là phương pháp duy nhất trong bộ điều khiển của tôi.
Normand Bedard

2

Nếu bạn không muốn đi theo ModelBinding, bạn có thể sử dụng DTO để làm điều này cho bạn. Ví dụ: tạo một hành động POST trong DataLayer chấp nhận một loại phức tạp và gửi dữ liệu từ BusinessLayer. Bạn có thể làm điều đó trong trường hợp UI-> API gọi.

Dưới đây là mẫu DTO. Chỉ định một giáo viên cho một học sinh và chỉ định nhiều giấy tờ / môn học cho học sinh.

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

Sau đó, hành động trong DataLayer có thể được tạo như sau:

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

Để gọi nó từ BusinessLayer:

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

Bây giờ điều này sẽ vẫn hoạt động nếu tôi muốn gửi dữ liệu của nhiều Sinh viên cùng một lúc. Sửa đổi MyActionnhư dưới đây. Không cần phải viết [FromBody], WebAPI2 lấy loại phức tạp [FromBody] theo mặc định.

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

và sau đó trong khi gọi nó, truyền một List<StudentCurriculumDTO>dữ liệu.

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

0

Yêu cầu tham số như

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

Mã api web giống như

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

Bạn có thể lấy formdata dưới dạng chuỗi:

    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/advified/seinating-html-form-data-part-2

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.