Beberapa metode HttpPost di pengontrol API Web

126

Saya mulai menggunakan proyek API Web MVC4, saya punya controller dengan banyak HttpPostmetode. Controller terlihat seperti berikut:

Pengendali

public class VTRoutingController : ApiController
{
    [HttpPost]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Di sini MyRequestTemplatemewakili kelas templat yang bertanggung jawab untuk menangani Json yang datang melalui permintaan.

Kesalahan:

Ketika saya membuat permintaan menggunakan Fiddler untuk http://localhost:52370/api/VTRouting/TSPRouteatau http://localhost:52370/api/VTRouting/Route saya mendapatkan kesalahan:

Beberapa tindakan ditemukan yang sesuai dengan permintaan

Jika saya menghapus salah satu metode di atas berfungsi dengan baik.

Global.asax

Saya telah mencoba memodifikasi tabel routing default global.asax, tetapi saya masih mendapatkan kesalahan, saya pikir saya memiliki masalah dalam mendefinisikan rute di global.asax. Inilah yang saya lakukan di global.asax.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "MyTSPRoute",
        routeTemplate: "api/VTRouting/TSPRoute",
        defaults: new { }
    );

    routes.MapHttpRoute(
        name: "MyRoute",
        routeTemplate: "api/VTRouting/Route",
        defaults: new { action="Route" }
    );
}

Saya membuat permintaan di Fiddler menggunakan POST, melewati json di RequestBody for MyRequestTemplate.

Habib
sumber

Jawaban:

143

Anda dapat memiliki beberapa tindakan dalam satu kontroler.

Untuk itu Anda harus melakukan dua hal berikut.

  • Pertama, hiasi tindakan dengan ActionNameatribut seperti

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
  • Kedua, tentukan rute berikut dalam WebApiConfigfile.

    // Controller Only
    // To handle routes like `/api/VTRouting`
    config.Routes.MapHttpRoute(
        name: "ControllerOnly",
        routeTemplate: "api/{controller}"               
    );
    
    
    // Controller with ID
    // To handle routes like `/api/VTRouting/1`
    config.Routes.MapHttpRoute(
        name: "ControllerAndId",
        routeTemplate: "api/{controller}/{id}",
        defaults: null,
        constraints: new { id = @"^\d+$" } // Only integers 
    );
    
    // Controllers with Actions
    // To handle routes like `/api/VTRouting/route`
    config.Routes.MapHttpRoute(
        name: "ControllerAndAction",
        routeTemplate: "api/{controller}/{action}"
    );
Asif Mushtaq
sumber
Bagaimana jika saya tidak ingin menetapkan batasan pada jenis ID? Artinya: bagaimana saya bisa menerima string ID juga?
frapontillo
5
@ frapontillo: Id harus berupa integeter, sehingga dipisahkan dari nama rute jika tidak, mesin routing akan memperlakukannya sebagai nama tindakan dan bukan id. Jika Anda perlu memiliki id sebagai string maka Anda dapat membuat tindakan.
Asif Mushtaq
Saya akan menggunakan Routing Atribut sebagai gantinya. Anda tidak perlu menggunakan banyak rute di WebApiConfig dengan cara itu. Lihat tautan ini: docs.microsoft.com/en-us/aspnet/web-api/overview/…
Kaya
Jika saya tambahkan seperti ini, ini memberi saya kesalahan ------------ namespace ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; set; }} [ActionName ("ProductController")] kelas publik ProductController: ApiController {// GET: api / NewCotroller public IEnumerable <string> Get () {kembalikan string baru [] {"value1", "value2"}; }
Umashankar
41

Solusi yang jauh lebih baik untuk masalah Anda adalah menggunakan Routeyang memungkinkan Anda menentukan rute pada metode dengan penjelasan:

[RoutePrefix("api/VTRouting")]
public class VTRoutingController : ApiController
{
    [HttpPost]
    [Route("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    [Route("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}
Wisienkas
sumber
Apa Namespace Route? Saya menggunakan MVC4 dan Rute tidak dikenali.
eaglei22
Ya, ini jalannya. Terima kasih.
pemain baru
1
untuk beberapa alasan saya tidak bisa mendapatkan ini berfungsi. ini persis apa yang sudah saya lakukan.
oligofren
2
Bagaimana url terlihat daripada menelepon Routedan TSPRoute?
Si8
27

menggunakan:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

itu bukan pendekatan RESTful lagi, tetapi Anda sekarang dapat memanggil tindakan Anda dengan nama (daripada membiarkan Web API secara otomatis menentukan satu untuk Anda berdasarkan kata kerja) seperti ini:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

Bertentangan dengan kepercayaan populer, tidak ada yang salah dengan pendekatan ini, dan itu tidak menyalahgunakan Web API. Anda masih dapat memanfaatkan semua fitur luar biasa dari Web API (mendelegasikan penangan, negosiasi konten, mediatype formatters dan sebagainya) - Anda baru saja membuang pendekatan RESTful.

Filipus W
sumber
1
Terima kasih atas jawabannya, tetapi masih memberi saya kesalahan yang sama.
Habib
Itu tidak mungkin, maka hal lain harus salah konfigurasi di aplikasi Anda. Bisakah Anda menunjukkan seluruh pengaturan Rute? Juga bagaimana tepatnya Anda memanggil tindakan pengendali?
Filip W
Seluruh pengaturan Rute berada di global.asax, saya telah memposting bagian itu dalam pertanyaan saya, Untuk membuat permintaan, saya menggunakan Fiddler-> Compose-> dan memilih Post sebagai operasinya
Habib
coba hapus semua definisi rute lain dan tinggalkan saja yang saya posting. Kemudian Anda dapat dengan mudah memanggil kedua tindakan POST yang terletak di dalam satu pengontrol (sama seperti pendekatan MVC lama)
Filip W
1
Filip, saya menggunakan .Net framework 4.5, dengan mvc4 atau Visual studio 2012 RC, Template mana yang Anda gunakan untuk membuat proyek Anda, milik Anda bekerja dengan sempurna
Habib
13

Titik api web (pengontrol) adalah satu sumber daya tunggal yang menerima kata kerja get / post / put / delete. Ini bukan pengontrol MVC normal.

Seharusnya, di /api/VTRoutingsana hanya ada satu metode HttpPost yang menerima parameter yang Anda kirim. Nama fungsi tidak masalah , selama Anda mendekorasi dengan barang-barang [http]. Tapi saya belum pernah mencoba.

Sunting: Ini tidak berfungsi. Dalam menyelesaikannya, tampaknya pergi dengan jumlah parameter, tidak mencoba model-bind ke tipe.

Anda dapat membebani fungsi untuk menerima parameter yang berbeda. Saya cukup yakin Anda akan baik-baik saja jika Anda menyatakannya seperti yang Anda lakukan, tetapi menggunakan parameter yang berbeda (tidak kompatibel) dengan metode. Jika paramsnya sama, Anda kurang beruntung karena penjilidan model tidak akan tahu yang Anda maksud.

[HttpPost]
public MyResult Route(MyRequestTemplate routingRequestTemplate) {...}

[HttpPost]
public MyResult TSPRoute(MyOtherTemplate routingRequestTemplate) {...}

Bagian ini berfungsi

Template default yang mereka berikan ketika Anda membuat yang baru membuatnya cukup eksplisit, dan saya akan mengatakan Anda harus tetap dengan konvensi ini:

public class ValuesController : ApiController
{
    // GET is overloaded here.  one method takes a param, the other not.
    // GET api/values  
    public IEnumerable<string> Get() { .. return new string[] ... }
    // GET api/values/5
    public string Get(int id) { return "hi there"; }

    // POST api/values (OVERLOADED)
    public void Post(string value) { ... }
    public void Post(string value, string anotherValue) { ... }
    // PUT api/values/5
    public void Put(int id, string value) {}
    // DELETE api/values/5
    public void Delete(int id) {}
}

Jika Anda ingin membuat satu kelas yang melakukan banyak hal, untuk penggunaan ajax, tidak ada alasan besar untuk tidak menggunakan pola pengontrol / tindakan standar. Satu-satunya perbedaan nyata adalah tanda tangan metode Anda tidak cantik, dan Anda harus membungkusnya Json( returnValue)sebelum mengembalikannya.

Edit:

Overloading berfungsi dengan baik saat menggunakan templat standar (diedit untuk disertakan) saat menggunakan tipe sederhana. Saya telah pergi dan menguji sebaliknya, dengan 2 objek khusus dengan tanda tangan yang berbeda. Tidak pernah bisa membuatnya bekerja.

Ini bekerja untuk saya dalam hal ini, lihat di mana itu membuat Anda. Pengecualian hanya untuk pengujian.

public class NerdyController : ApiController
{
    public void Post(string type, Obj o) { 
        throw new Exception("Type=" + type + ", o.Name=" + o.Name ); 
    }
}

public class Obj {
    public string Name { get; set; }
    public string Age { get; set; }
}

Dan disebut seperti ini dari konsol:

$.post("/api/Nerdy?type=white", { 'Name':'Slim', 'Age':'21' } )
Andrew Backer
sumber
Saya telah mencoba mengubah tipe parameter, tetapi sepertinya hanya memungkinkan metode Post tunggal di controller. Terima kasih atas balasan Anda
Habib
Saya berasumsi bahwa itu akan mencoba model yang mengikat untuk menemukannya, karena Anda dapat kelebihan. Ini bekerja dengan # par yang berbeda. Mungkin tidak sulit untuk menulis ulang untuk melakukan ini, tetapi mereka belum merilis kode sumber, jadi saya hanya terjebak melihat pembongkaran yang jelek
Andrew Backer
2
+1 untuk benar-benar menjelaskan alasan itu tidak berfungsi, dan filosofi di balik api web.
MEMark
Menghargai breakdown ... Saya berasumsi itu seharusnya menjadi satu POST / PUT / GET per controller tapi saya tidak yakin ... jadi alasan mengapa saya mencarinya. Sejak saya mulai mengembangkan dengan MVC untuk aplikasi web di mana beberapa tindakan seperti per pengontrol adalah hal yang biasa ... sepertinya sia-sia jadi saya bisa mengerti mengapa seorang pengembang menginginkannya. Apakah ada terlalu banyak pengendali?
Anthony Griggs
6

Dimungkinkan untuk menambahkan beberapa metode Get dan Post di Web API Controller yang sama. Di sini Rute default yang Menyebabkan Masalah. API Web memeriksa Rute Pencocokan dari Atas ke Bawah dan Karenanya Rute Default Anda Cocok untuk semua Permintaan. Sesuai rute default, hanya satu Metode Get and Post yang dimungkinkan dalam satu controller. Baik tempatkan kode berikut di atas atau Komentar Keluar / Hapus Rute Default

    config.Routes.MapHttpRoute("API Default", 
                               "api/{controller}/{action}/{id}",
                               new { id = RouteParameter.Optional });
Shahid Ullah
sumber
1

Letakkan Rute Awalan [RoutePrefix ("api / Profiles")] di tingkat pengontrol dan letakkan rute di metode tindakan [Rute ("SukaProfil")]. Tidak perlu mengubah apa pun di file global.asax

namespace KhandalVipra.Controllers
{
    [RoutePrefix("api/Profiles")]
    public class ProfilesController : ApiController
    {
        // POST: api/Profiles/LikeProfile
        [Authorize]
        [HttpPost]
        [Route("LikeProfile")]
        [ResponseType(typeof(List<Like>))]
        public async Task<IHttpActionResult> LikeProfile()
        {
        }
    }
}
Sushil Kumar
sumber
0

Saya pikir pertanyaannya sudah dijawab. Saya juga mencari sesuatu pengontrol webApi yang memiliki mehtods yang sama tetapi nama yang berbeda. Saya mencoba menerapkan Kalkulator sebagai WebApi. Kalkulator memiliki 4 metode dengan tanda tangan yang sama tetapi nama yang berbeda.

public class CalculatorController : ApiController
{
    [HttpGet]
    [ActionName("Add")]
    public string Add(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Add = {0}", num1 + num2);
    }

    [HttpGet]
    [ActionName("Sub")]
    public string Sub(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Subtract result = {0}", num1 - num2);
    }

    [HttpGet]
    [ActionName("Mul")]
    public string Mul(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Multiplication result = {0}", num1 * num2);
    }

    [HttpGet]
    [ActionName("Div")]
    public string Div(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Division result = {0}", num1 / num2);
    }
}

dan dalam file WebApiConfig yang sudah Anda miliki

 config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional });

Cukup atur otentikasi / otorisasi pada IIS dan Anda selesai!

Semoga ini membantu!

Yawar Murtaza
sumber
0

Anda dapat menggunakan pendekatan ini:

public class VTRoutingController : ApiController
{
    [HttpPost("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}
Amirhossein Yari
sumber
-1
public class Journal : ApiController
{
    public MyResult Get(journal id)
    {
        return null;
    }
}

public class Journal : ApiController
{

    public MyResult Get(journal id, publication id)
    {
        return null;
    }
}

Saya tidak yakin apakah metode get / post overloading melanggar konsep restfull api, tetapi workds. Kalau ada yang bisa mencerahkan tentang hal ini. Bagaimana jika saya memiliki uri sebagai

uri:/api/journal/journalid
uri:/api/journal/journalid/publicationid

jadi seperti yang Anda lihat jurnal saya semacam aggregateroot, meskipun saya dapat menentukan pengontrol lain untuk publikasi semata-mata dan memberikan nomor publikasi pada url saya, tetapi ini memberi jauh lebih masuk akal. karena publikasi saya tidak akan ada tanpa jurnal itu sendiri.

mobygeek
sumber
-1

Saya baru saja menambahkan "action = action_name" ke url dan dengan cara ini mesin perutean tahu tindakan apa yang saya inginkan. Saya juga menambahkan Atribut ActionName ke tindakan tetapi saya tidak yakin itu diperlukan.

Rony Tesler
sumber
-1

Penjelasan terbaik dan paling sederhana yang saya lihat pada topik ini - http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • Diedit -

Saya berhasil hanya dengan Route, dan tidak perlu RoutePrefix.

Misalnya, di controller

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomer
([FromBody]CustomerOrder obj)
{
}

dan

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}

Kemudian, nama fungsi digunakan dalam jquery sebagai -

options.url = "/api/customer/PostCustomer";

atau

options.url = "/api/customer/PostCustomerAndOrder";
Howard Shlom
sumber