Perutean API Web - api / {controller} / {action} / {id} api “dysfunctions” / {controller} / {id}

94

Saya memiliki Route default di Global.asax:

 RouteTable.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

Saya ingin dapat menargetkan fungsi tertentu, jadi saya membuat rute lain:

RouteTable.Routes.MapHttpRoute(
         name: "WithActionApi",
         routeTemplate: "api/{controller}/{action}/{id}",
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

Jadi, di pengontrol saya, saya memiliki:

    public string Get(int id)
    {
        return "object of id id";
    }        

    [HttpGet]
    public IEnumerable<string> ByCategoryId(int id)
    {
        return new string[] { "byCategory1", "byCategory2" };
    }

Menelepon .../api/records/bycategoryid/5akan memberi saya apa yang saya inginkan. Namun, menelepon .../api/records/1akan memberi saya kesalahan

Beberapa tindakan ditemukan yang sesuai dengan permintaan: ...

Saya mengerti mengapa itu - rute hanya menentukan URL apa yang valid, tetapi ketika datang ke pencocokan fungsi, keduanya Get(int id)dan ByCategoryId(int id)cocok api/{controller}/{id}, itulah yang membingungkan kerangka kerja.

Apa yang harus saya lakukan agar rute API default berfungsi kembali, dan mempertahankannya {action}? Saya berpikir untuk membuat pengontrol berbeda yang diberi nama RecordByCategoryIdControlleragar sesuai dengan rute API default, yang akan saya minta .../api/recordbycategoryid/5. Namun, saya menemukan itu menjadi solusi yang "kotor" (dengan demikian tidak memuaskan). Saya telah mencari jawaban tentang ini dan tidak ada tutorial di luar sana tentang menggunakan rute {action}bahkan dengan menyebutkan masalah ini.

Mickael Caruso
sumber

Jawaban:

104

Mesin rute menggunakan urutan yang sama saat Anda menambahkan aturan ke dalamnya. Setelah mendapatkan aturan pertama yang cocok, itu akan berhenti memeriksa aturan lain dan mengambil ini untuk mencari pengontrol dan tindakan.

Jadi, Anda harus:

  1. Tempatkan aturan spesifik Anda di depan aturan umum Anda (seperti default), yang berarti gunakan RouteTable.Routes.MapHttpRouteuntuk memetakan "WithActionApi" terlebih dahulu, lalu "DefaultApi".

  2. Hapus defaults: new { id = System.Web.Http.RouteParameter.Optional }parameter aturan "WithActionApi" Anda karena setelah id bersifat opsional, url seperti "/ api / {part1} / {part2}" tidak akan pernah masuk ke "DefaultApi".

  3. Tambahkan tindakan bernama ke "DefaultApi" Anda untuk memberi tahu mesin rute tindakan mana yang harus dimasukkan. Jika tidak, setelah Anda memiliki lebih dari satu tindakan di pengontrol Anda, mesin tidak akan tahu tindakan mana yang akan digunakan dan menampilkan "Beberapa tindakan ditemukan yang cocok dengan permintaan: ...". Kemudian untuk membuatnya cocok dengan metode Get Anda, gunakan ActionNameAttribute .

Jadi, rute Anda harus seperti ini:

// Map this rule first
RouteTable.Routes.MapRoute(
     "WithActionApi",
     "api/{controller}/{action}/{id}"
 );

RouteTable.Routes.MapRoute(
    "DefaultApi",
    "api/{controller}/{id}",
    new { action="DefaultAction", id = System.Web.Http.RouteParameter.Optional }
);

Dan pengontrol Anda:

[ActionName("DefaultAction")] //Map Action and you can name your method with any text
public string Get(int id)
{
    return "object of id id";
}        

[HttpGet]
public IEnumerable<string> ByCategoryId(int id)
{
    return new string[] { "byCategory1", "byCategory2" };
}
Chris Li
sumber
1
Saya mencoba saran di atas dan semuanya berjalan sesuai harapan. Terima kasih banyak atas "rahasia" itu.
Mickael Caruso
Pada poin 2 jawaban Anda, jika idopsional maka URL like /api/{part1}/{part2}mungkin masih masuk ke DefaultApirute jika tidak ditemukan tindakan yang cocok untuk WithActionApirute. Harap perbaiki saya jika saya salah.
orad
apa yang terjadi jika saya hanya ingin memiliki api / {controller} / {action}. tanpa id. Jika orang lain menghadapi masalah yang sama. lupakan WebApiConfig. baca tentang Perutean Atribut.
ahsant
40

Anda dapat mengatasi masalah Anda dengan bantuan perutean atribut

Kontroler

[Route("api/category/{categoryId}")]
public IEnumerable<Order> GetCategoryId(int categoryId) { ... }

URI di jquery

api/category/1

Konfigurasi Rute

using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}

dan perutean default Anda berfungsi sebagai perutean berbasis konvensi default

Kontroler

public string Get(int id)
    {
        return "object of id id";
    }   

URI di Jquery

/api/records/1 

Konfigurasi Rute

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Attribute routing.
        config.MapHttpAttributeRoutes();

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

Tinjau artikel untuk informasi lebih lanjut Perutean atribut dan perutean berbasis konvensi di sini & ini

MSTdev
sumber
jawaban tuhan. tapi tidak bisakah kita menambahkan untuk semua, api / {controller} / {action} / {id} bersama dengan api / {controller} / {id}?
karim
Atribut routing jelas memecahkan masalah. Satu poin penting: Sebelum Web API 2, template proyek API Web menghasilkan kode seperti ini: protected void Application_Start () {WebApiConfig.Register (GlobalConfiguration.Configuration); } Jika perutean atribut diaktifkan, kode ini akan memunculkan pengecualian. Jika Anda memutakhirkan proyek API Web yang ada untuk menggunakan perutean atribut, pastikan untuk memperbarui kode konfigurasi ini ke yang berikut: protected void Application_Start () {GlobalConfiguration.Configure (WebApiConfig.Register); }
Deeb
0

Coba ini.

public class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        var json = config.Formatters.JsonFormatter;
        json.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
        config.Formatters.Remove(config.Formatters.XmlFormatter);

        // Web API routes
        config.MapHttpAttributeRoutes();

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

        );
    }
}
Amit
sumber
0

Kemungkinan alasannya juga karena Anda belum mewarisi Controller dari ApiController. Terjadi dengan saya butuh beberapa saat untuk memahami hal yang sama.

Paritosh Tiwari
sumber
0

Untuk membedakan rute, coba tambahkan batasan bahwa id harus numerik:

RouteTable.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         constraints: new { id = @"\d+" }, // Only matches if "id" is one or more digits.
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );  
Derek Wade
sumber