Параметри декількох "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.