Beberapa parameter Put / Post WebAPI

154

Saya mencoba memposting beberapa parameter pada pengontrol WebAPI. Satu param adalah dari URL, dan yang lainnya dari tubuh. Ini urlnya: /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

Ini kode pengontrol saya:

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

Konten tubuh ada di 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
                        }]
                    }
                }]
            }
        }]
    }
}

Adakah yang tahu mengapa pengikatan default tidak dapat mengikat offerPriceParametersargumen controller saya? Itu selalu diatur ke nol. Tetapi saya dapat memulihkan data dari tubuh menggunakan DataContractJsonSerializer.

Saya juga mencoba menggunakan FromBodyatribut argumen tetapi tidak berhasil juga.

Normand Bedard
sumber

Jawaban:

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

menggunakan referensi

using Newtonsoft.Json.Linq;

Gunakan Permintaan untuk 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);
    }
});
Fatih GÜRDAL
sumber
3
Solusi bagus Jika belum jelas bagi orang lain, Anda juga dapat menggunakan .ToObject <int> (), .ToObject <decimal> (), .ToString (), dll. Jika Anda mengirimkan secara sederhana, beberapa parameter dari panggilan ajax Anda.
secretwep
Terima kasih, saya telah mencoba solusi Anda dengan membuat API saya sendiri dan mengujinya melalui tukang pos dan berfungsi dengan baik, tetapi saya telah menambahkan parameter keempat seperti var test = {"Name": "test"} dan menambahkannya ke objek myData dan berhasil dikirim; apakah ada cara untuk menghindari ini dan hanya membatasi objek asli?
Mlle116
@ H.Al Tidak, Newtonsoft.Json dapat memiliki jenis data json apa pun yang diketahui perpustakaan tentang terjemahan. Anda tidak dapat mencegah pengiriman data. Tergantung pada Anda untuk menggunakan data yang masuk
Fatih GÜRDAL
63

Native WebAPI tidak mendukung pengikatan beberapa parameter POST. Seperti yang ditunjukkan oleh Colin, ada sejumlah batasan yang diuraikan dalam posting blog saya sebagai referensi.

Ada solusi dengan membuat pengikat parameter khusus. Kode untuk melakukan ini jelek dan berbelit-belit, tetapi saya telah memposting kode bersama dengan penjelasan terperinci di blog saya, siap untuk dihubungkan ke proyek di sini:

Melewati beberapa Nilai POST sederhana ke ASP.NET Web API

Rick Strahl
sumber
1
Semua kredit jatuh kepada Anda :) Saya kebetulan membaca seri Anda di WebAPI sambil memulai implementasi saya sendiri ketika pertanyaan ini muncul.
Colin Young
Terima kasih! Sangat membantu.
Normand Bedard
2
Pada 2019 sekarang.
Maks
@ John - apakah ada versi dasar dari mana fungsi ini didukung? Tidak ada kesuksesan hari ini.
Neil Moss
26

Jika perutean atribut sedang digunakan, Anda dapat menggunakan atribut [FromUri] dan [FromBody].

Contoh:

[HttpPost()]
[Route("api/products/{id:int}")]
public HttpResponseMessage AddProduct([FromUri()] int id,  [FromBody()] Product product)
{
  // Add product
}
Bryan Rayner
sumber
1
Saya telah menggunakan metode yang persis sama. Saya perlu memberikan dua Model ke aksi. Saya telah melewati satu dengan properti lebih sedikit melalui string kueri dan lainnya dari tubuh. Anda juga tidak perlu menentukan secara spesifik attribyte [FromBody]
Sergey G.
1
Saya tidak bisa membuat ini bekerja, apakah Anda memiliki contoh yang lebih lengkap?
The One
Saya tidak berpikir ini adalah cara yang tepat untuk mengirim data melalui metode POST tetapi saya tidak melihat solusi lain jika Anda harus mengirim 2 model melalui pos.
Alexandr
Jawaban ini adalah Jam!
Leonardo Wildt
1
Saya menggunakan aspnetcore dan Anda harus menggunakan [FromRoute]bukannya[FromUri]
DanielV
19

Kami melewati objek Json dengan metode HttpPost, dan menguraikannya dalam objek dinamis. ini bekerja dengan baik. ini adalah contoh kode:

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

   ...
}

Jenis objek yang kompleks bisa berupa objek, array, dan kamus.

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"
           }]
       }}
...
Bes Ley
sumber
1
Kita dapat menempatkan beberapa parameter yang diformat sebagai satu objek json untuk dikirim, dan kita akan menguraikannya ke beberapa objek di sisi server nanti. Ini bisa menjadi cara lain untuk berpikir.
Bes Ley
@EkoosticMartin, Ini berfungsi dengan baik, Anda perlu mengurai tipe dinamis dengan menggunakan: JsonConvert.DeserializeObject <YourObjectTypeHere> (data.ToString ()); Sampel konten data yang kompleks ada di sini, termasuk array dan objek kamus. {"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
Oke pasti, kalau begitu gunakan param string tunggal, tidak ada perbedaan.
EkoostikMartin
Tunggal tidak berarti string json sederhana dapat dikombinasikan dengan berbagai jenis objek. Ini adalah titik kunci, dan merupakan cara lain untuk menyelesaikan pertanyaan.
Bes Ley
1
Solusi yang sangat baik! Terima kasih :)
Carl R
10

Kelas parameter sederhana dapat digunakan untuk melewatkan beberapa parameter dalam sebuah posting:

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

[HttpPost]
public IHttpActionResult AddCustomer(AddCustomerArgs args)
{
    //use args...
    return Ok();
}
Greg Gum
sumber
Apakah ada kemungkinan Anda tahu bagaimana sampel permintaan POST seharusnya?
Nadia Solovyeva
@NadiaSolovyeva, Ini lebih dari string kueri, karena informasi POSTED ada di dalam tubuh, bukan string kueri. Saya suka menggunakan PostMan untuk membuat pertanyaan pengujian, dan kemudian Anda bisa melihat seperti apa tampilannya.
Greg Gum
Sudahlah, saya sudah menemukan cara melakukannya. Header POST: Tipe-Konten: application / json; Badan POST: {"Pertama": "1", "Terakhir": "1000"}
Nadia Solovyeva
9

Anda dapat mengizinkan beberapa parameter POST dengan menggunakan kelas MultiPostParameterBinding dari https://github.com/keith5000/MultiPostParameterBinding

Untuk menggunakannya:

1) Unduh kode di folder Sumber dan tambahkan ke proyek API Web Anda atau proyek lain dalam solusinya.

2) Gunakan atribut [MultiPostParameters] pada metode tindakan yang perlu mendukung beberapa parameter POST.

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

3) Tambahkan baris ini di Global.asax.cs ke metode Application_Start di mana saja sebelum panggilan ke GlobalConfiguration.Configure (WebApiConfig.Register) :

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

4) Minta klien Anda melewati parameter sebagai properti objek. Contoh objek JSON untuk DoSomething(param1, param2, param3)metode ini adalah:

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

Contoh JQuery:

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

Kunjungi tautannya untuk detail lebih lanjut.

Penafian: Saya terkait langsung dengan sumber daya yang ditautkan.

Keith
sumber
7

Pertanyaan dan komentar yang bagus - banyak belajar dari balasan di sini :)

Sebagai contoh tambahan, perhatikan bahwa Anda juga dapat mencampur tubuh dan rute misalnya

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

Memanggil seperti ini:

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
Anthony De Souza
sumber
Saya ingin mengirim 2 parameter tipe kompleks. Seperti halnya [HttpPost] string publik UploadFile (UploadMediaFile mediaFile, byte [] data) bagaimana melakukannya.
Başar Kaya
2

Seperti apa routeTemplate Anda untuk kasus ini?

Anda memposting url ini:

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

Agar ini berfungsi, saya harapkan perutean seperti ini di WebApiConfig:

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

Asumsi lain adalah: - controller Anda dipanggil OffersController. - objek JSON yang Anda lewati di badan permintaan bertipe OfferPriceParameters(bukan tipe turunan) - Anda tidak memiliki metode lain pada pengontrol yang dapat mengganggu yang ini (jika Anda melakukannya, coba beri komentar dan lihat apa terjadi)

Dan seperti yang Filip katakan akan membantu pertanyaan Anda jika Anda mulai menerima beberapa jawaban karena 'terima tingkat 0%' mungkin membuat orang berpikir bahwa mereka membuang-buang waktu mereka

Joanna Derks
sumber
Rute saya adalah "penawaran / {offerId} / harga". Ini adalah satu-satunya metode di controller saya.
Normand Bedard
2

Jika Anda tidak ingin menggunakan ModelBinding, Anda dapat menggunakan DTO untuk melakukan ini untuk Anda. Misalnya, buat tindakan POST di DataLayer yang menerima tipe kompleks dan mengirim data dari BusinessLayer. Anda dapat melakukannya jika ada panggilan UI-> API.

Berikut adalah contoh DTO. Tetapkan Guru untuk Siswa dan Tetapkan beberapa makalah / subjek untuk Siswa.

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

Maka tindakan dalam DataLayer dapat dibuat sebagai:

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

Untuk memanggilnya dari BusinessLayer:

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

Sekarang ini masih akan berfungsi jika saya ingin mengirim data beberapa Siswa sekaligus. Ubah MyActionseperti di bawah ini. Tidak perlu menulis [FromBody], WebAPI2 menggunakan tipe kompleks [FromBody] secara default.

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

dan kemudian saat memanggilnya, berikan List<StudentCurriculumDTO>data.

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

Parameter permintaan seperti

masukkan deskripsi gambar di sini

Kode api web menjadi seperti

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();
}
Pradip Rupareliya
sumber
0

Anda bisa mendapatkan formdata sebagai string:

    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

Martien de Jong
sumber