Параметри декількох "Put / Post" WebAPI


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 = {"Name": "test"} і додав його до об'єкта myData і воно було надіслано успішно; чи все-таки уникати цього і обмежувати лише оригінальні об’єкти?
Mlle116

@ H.Al Ні, Newtonsoft.Json може мати будь-які дані json, які бібліотека знає про переклад. Не можна запобігти надсиланню даних. Від вас залежить використання вхідних даних
Fatih GÜRDAL

63

Native WebAPI не підтримує прив'язку декількох параметрів POST. Як зазначає Колін, існує ряд обмежень, які викладені в моїй публікації в блозі, яку він посилається.

Існує вирішення, створивши прив'язку спеціального параметра. Код для цього некрасивий і заплутаний, але я розмістив код разом із детальним поясненням у своєму блозі, готовий бути включеним у проект тут:

Передача кількох простих значень POST у веб-API ASP.NET


1
Вся заслуга вам належить :) Мені просто трапилось читати вашу серію в WebAPI, починаючи мою власну реалізацію, коли з'явилося це питання.
Колін Янг

Дякую! Дуже корисний.
Норманд Бедард

2
Станом на 2019 рік це робить і зараз.
Макс

@John - чи є базова версія, в якій підтримується ця функціональність? Не маючи сьогодні жодного успіху.
Ніл Мосс

26

Якщо використовується маршрутизація атрибутів, ви можете використовувати атрибути [FromUri] та [FromBody].

Приклад:

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

1
Я використовував абсолютно той самий метод. Мені потрібно передати дві моделі до дії. Я передав один з меншими властивостями через рядок запиту, а інший - від body. Також вам не потрібно чітко вказувати атрибут [FromBody]
Сергій Г.

1
Я не можу зробити цю роботу, у вас є більш повний приклад?
Єдиний

Я не думаю, що це правильний спосіб надсилання даних методом POST, але я не бачу іншого рішення, якщо вам доведеться надсилати 2 моделі поштою.
Олександр

Ця відповідь - варення!
Леонардо Уайлдт

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", "UserName": "Jack", " NextActivityPerformers ": {" 39c71004-d822-4c15-9ff2-94ca1068d745 ": [{" UserID ": 10," UserName ":" Smith "}]}}
Bes Ley

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-запиту?
Надія Соловйова

@NadiaSolovyeva, Це більше, ніж рядок запиту, тому що розміщена інформація знаходиться в тілі, а не в рядку запиту. Мені подобається використовувати PostMan для тестових запитів, і тоді ви можете точно побачити, як це виглядає.
Грег Гум

Неважливо, я вже знайшов, як це зробити. Заголовок POST: Content-Type: application / json; Корпус пошти: {"Перший": "1", "Останній": "1000"}
Надія Соловйова

9

Ви можете дозволити кілька параметрів POST, використовуючи клас MultiPostParameterBinding з https://github.com/keith5000/MultiPostParameterBinding

Щоб використовувати його:

1) Завантажте код у папці Source та додайте його до свого проекту 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, байт [] дані), як це зробити.
Башар Кая

2

Як виглядає ваш маршрутTemplate в цьому випадку?

Ви опублікували цю URL-адресу:

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

Для того, щоб це працювало, я очікую такої маршрутизації у вашому WebApiConfig:

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

Інші припущення: - викликається ваш контролер OffersController. - тип об'єкта JSON, який ви передаєте в тілі запитуOfferPriceParameters (не будь-який похідний тип) - у вас немає інших методів на контролері, які могли б заважати цьому (якщо ви це зробите, спробуйте прокоментувати їх і подивіться, що трапляється)

І як згадував Філіп, це допоможе вашим питанням, якщо ви почнете приймати відповіді як "прийняти ставку 0%", можливо, люди змусять задуматися, що вони витрачають свій час


Мій маршрут - "пропозиції / {offerId} / ціни". Це єдиний метод у моєму контролері.
Норманд Бедард

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

Запитайте такі параметри, як

введіть тут опис зображення

Код веб-api бути таким

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

Ви можете отримати форматні дані як рядкові:

    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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.