Nama metode kustom di ASP.NET Web API

110

Saya mengubah dari WCF Web API ke ASP.NET MVC 4 Web API baru. Saya memiliki UsersController, dan saya ingin memiliki metode bernama Authenticate. Saya melihat contoh bagaimana melakukan GetAll, GetOne, Post, dan Delete, namun bagaimana jika saya ingin menambahkan metode ekstra ke dalam layanan ini? Misalnya, UsersService saya harus memiliki metode yang disebut Autentikasi di mana mereka meneruskan nama pengguna dan kata sandi, namun itu tidak berhasil.

public class UsersController : BaseApiController
{
    public string GetAll()
    {
        return "getall!";
    }

    public string Get(int id)
    {
        return "get 1! " + id;
    }

    public User GetAuthenticate(string userName, string password, string applicationName)
    {
        LogWriter.Write(String.Format("Received authenticate request for username {0} and password {1} and application {2}",
            userName, password, applicationName));

        //check if valid leapfrog login.
        var decodedUsername = userName.Replace("%40", "@");
        var encodedPassword = password.Length > 0 ? Utility.HashString(password) : String.Empty;
        var leapFrogUsers = LeapFrogUserData.FindAll(decodedUsername, encodedPassword);

        if (leapFrogUsers.Count > 0)
        {
            return new User
            {
                Id = (uint)leapFrogUsers[0].Id,
                Guid = leapFrogUsers[0].Guid
            };
        }
        else
            throw new HttpResponseException("Invalid login credentials");
    }
}

Saya dapat mengakses myapi / api / users / dan itu akan memanggil GetAll dan saya dapat menjelajah ke myapi / api / users / 1 dan itu akan memanggil Get, namun jika saya memanggil myapi / api / users / authenticate? Username = {0} & password = {1} maka itu akan memanggil Get (NOT Authenticate) dan kesalahan:

Kamus parameter berisi entri null untuk parameter 'id' tipe non-nullable 'System.Int32' untuk metode 'System.String Get (Int32)' di 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'. Parameter opsional harus berupa tipe referensi, tipe nullable, atau dideklarasikan sebagai parameter opsional.

Bagaimana cara memanggil nama metode kustom seperti Autentikasi?

Justin
sumber
Silakan lihat tautan ini: jawaban ke-5 stackoverflow.com/questions/12775590/…
Vishwa G

Jawaban:

137

Secara default konfigurasi rute mengikuti konvensi RESTFul yang berarti hanya akan menerima nama aksi Get, Post, Put dan Delete (lihat rute di global.asax => secara default tidak memungkinkan Anda untuk menentukan nama aksi => itu menggunakan kata kerja HTTP untuk mengirimkan). Jadi, ketika Anda mengirim permintaan GET kepada /api/users/authenticateAnda pada dasarnya memanggil Get(int id)tindakan dan passing id=authenticateyang jelas macet karena tindakan Get Anda mengharapkan integer.

Jika Anda ingin memiliki nama tindakan yang berbeda dari yang standar, Anda dapat mengubah definisi rute Anda di global.asax:

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

Sekarang Anda dapat menavigasi /api/values/getauthenticateuntuk mengautentikasi pengguna.

Darin Dimitrov
sumber
20
Adakah cara untuk membuatnya tetap menggunakan Get (id), Get () Put, Delete, Post sementara masih mengizinkan tindakan lain?
Shawn Mclean
@ShawnMclean Saya rasa Anda dapat menentukan rute lain tanpa {action}itu memiliki batasan {id}sehingga apa pun selain intatau Guid(atau apa pun) tidak akan cocok. Maka itu harus bisa jatuh ke yang disarankan oleh Darin
Steve Greatrex
2
Satu hal yang lebih penting di sini adalah dengan gaya perutean ini, Anda harus menggunakan atribut untuk menentukan metode HTTP yang diizinkan (seperti [HttpGet]).
Himalaya Garg
1
Anda yakin perlu menggunakan tindakan lain? Apakah Anda benar-benar mencoba menyesuaikan apa yang Anda lakukan dalam konvensi REST? Seharusnya tidak perlu menggunakan tindakan lain.
niico
1
@niico: Bayangkan Anda ingin memiliki metode Count (), yang mengembalikan jumlah elemen yang akan dikembalikan Get (). Saya tidak melihat bagaimana menyesuaikannya dengan Get (), Get (id), Post (...), Put (...) atau Delete (id). Dan, tentu saja, ada metode yang lebih mungkin dan tidak pasti yang bisa saya bayangkan.
Jens Mander
88

Ini adalah metode terbaik yang sejauh ini saya temukan untuk memasukkan metode GET tambahan sambil mendukung metode REST normal juga. Tambahkan rute berikut ke WebApiConfig Anda:

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

Saya memverifikasi solusi ini dengan kelas pengujian di bawah. Saya berhasil mencapai setiap metode di pengontrol saya di bawah ini:

public class TestController : ApiController
{
    public string Get()
    {
        return string.Empty;
    }

    public string Get(int id)
    {
        return string.Empty;
    }

    public string GetAll()
    {
        return string.Empty;
    }

    public void Post([FromBody]string value)
    {
    }

    public void Put(int id, [FromBody]string value)
    {
    }

    public void Delete(int id)
    {
    }
}

Saya memverifikasi bahwa itu mendukung permintaan berikut:

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

Catatan Jika tindakan GET ekstra Anda tidak dimulai dengan 'Get', Anda mungkin ingin menambahkan atribut HttpGet ke metode tersebut.

sky-dev
sumber
1
solusi yang bagus, dapatkah Anda memberi tahu saya jika saya mengonfigurasi kata kerja putand deleteseperti yang Anda lakukan getdan post, akan berfungsi dengan baik juga?
Felipe Oriani
1
Menurut pendapat saya ini harus dimasukkan dalam default untuk proyek WebAPI (mungkin diberi komentar). Ini memberi Anda rute gaya WebAPI DAN MVC pada saat yang sama ...
John Culviner
1
@FelipeOriani, saya rasa Anda tidak ingin atau perlu mengkonfigurasi putatau deletekata kerja karena permintaan tersebut biasanya akan menyertai parameter id untuk mengidentifikasi sumber daya yang ingin Anda terapkan operasi itu. Sebuah deletepanggilan untuk /api/fooharus melempar kesalahan karena yang foo yang Anda mencoba untuk menghapus? Oleh karena itu, rute DefaultApiWithId harus menangani kasus tersebut dengan baik.
nwayve
4
ini sama sekali tidak berhasil bagi saya. mendapat pesan kesalahan ketika saya mencoba melakukan GET dasar.
Matt
Untuk yang pertama, DefaultApiWithId, bukankah seharusnya defaultnya null, bukan {id = RouteParameter.Optional} baru? Bukankah 'id' diperlukan?
Johnny Oshika
22

Saya hari-hari ke dunia MVC4.

Untuk apa nilainya, saya memiliki SitesAPIController, dan saya membutuhkan metode khusus, yang bisa disebut seperti:

http://localhost:9000/api/SitesAPI/Disposition/0

Dengan nilai yang berbeda untuk parameter terakhir mendapatkan record dengan disposisi yang berbeda.

Apa yang Akhirnya berhasil untuk saya adalah:

Metode di SitesAPIController:

// GET api/SitesAPI/Disposition/1
[ActionName("Disposition")]
[HttpGet]
public Site Disposition(int disposition)
{
    Site site = db.Sites.Where(s => s.Disposition == disposition).First();
    return site;
}

Dan ini di WebApiConfig.cs

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

// this i added
config.Routes.MapHttpRoute(
    name: "Action",
    routeTemplate: "api/{controller}/{action}/{disposition}"
 );

Selama saya menamai {disposition} sebagai {id} yang saya temui:

{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:9000/api/SitesAPI/Disposition/0'.",
"MessageDetail": "No action was found on the controller 'SitesAPI' that matches the request."
}

Ketika saya mengganti namanya menjadi {disposition}, itu mulai bekerja. Jadi ternyata nama parameter tersebut cocok dengan nilai di placeholder.

Jangan ragu untuk mengedit jawaban ini agar lebih akurat / jelas.

Kinjal Dixit
sumber
Terima kasih atas tipnya. Saya membuat kesalahan yang sama seperti Anda.
abhi
16

Api Web secara default mengharapkan URL dalam bentuk api / {controller} / {id}, untuk mengganti perutean default ini. Anda dapat mengatur perutean dengan salah satu dari dua cara di bawah ini.

Pilihan pertama:

Tambahkan di bawah pendaftaran rute di WebApiConfig.cs

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

Hiasi metode tindakan Anda dengan HttpGet dan parameter seperti di bawah ini

[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
                        string param2, string param3)

 {

// your code here

}

untuk memanggil url metode di atas akan seperti di bawah ini

http: // localhost: [yourport] / api / MyData / ReadMyData? param1 = nilai1 & param2 = nilai2 & param3 = nilai3

Opsi kedua Tambahkan awalan rute ke kelas Controller dan Hiasi metode tindakan Anda dengan HttpGet seperti di bawah ini. Dalam hal ini, WebApiConfig.cs tidak perlu diubah. Itu dapat memiliki perutean default.

[RoutePrefix("api/{controller}/{action}")]
public class MyDataController : ApiController
{

[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
                        string param2, string param3)

{

// your code here

}

}

untuk memanggil url metode di atas akan seperti di bawah ini

http: // localhost: [yourport] / api / MyData / ReadMyData? param1 = nilai1 & param2 = nilai2 & param3 = nilai3

Nagaraju Mengani
sumber
Saya sangat menyukai opsi kedua. Bisakah Anda juga menunjukkan kepada saya cara menggunakannya di VB.net? Terima kasih banyak.
pengguna1617676
12

Jika Anda menggunakan ASP.NET 5 dengan ASP.NET MVC 6 , sebagian besar jawaban ini tidak akan berfungsi karena Anda biasanya akan membiarkan MVC membuat kumpulan rute yang sesuai untuk Anda (menggunakan konvensi RESTful default), yang berarti bahwa Anda tidak akan menemukan Routes.MapRoute()panggilan untuk mengedit sesuka Anda.

The ConfigureServices()metode dipanggil oleh Startup.csfile yang akan mendaftarkan MVC dengan kerangka Injeksi Ketergantungan dibangun ke ASP.NET 5: dengan cara itu, ketika Anda menelepon ApplicationBuilder.UseMvc()kemudian di kelas itu, kerangka MVC secara otomatis akan menambahkan rute default ini untuk aplikasi Anda. Kita dapat melihat apa yang terjadi di balik kap mesin dengan melihat UseMvc()penerapan metode dalam kode sumber kerangka kerja:

public static IApplicationBuilder UseMvc(
    [NotNull] this IApplicationBuilder app,
    [NotNull] Action<IRouteBuilder> configureRoutes)
{
    // Verify if AddMvc was done before calling UseMvc
    // We use the MvcMarkerService to make sure if all the services were added.
    MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

    var routes = new RouteBuilder
    {
        DefaultHandler = new MvcRouteHandler(),
        ServiceProvider = app.ApplicationServices
    };

    configureRoutes(routes);

    // Adding the attribute route comes after running the user-code because
    // we want to respect any changes to the DefaultHandler.
    routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
        routes.DefaultHandler,
        app.ApplicationServices));

    return app.UseRouter(routes.Build());
}

Hal yang baik tentang ini adalah bahwa kerangka sekarang menangani semua kerja keras, mengulangi semua Tindakan Pengontrol dan mengatur rute default mereka, sehingga menghemat beberapa pekerjaan yang berlebihan.

Hal buruknya adalah, ada sedikit atau tidak ada dokumentasi tentang bagaimana Anda dapat menambahkan rute Anda sendiri. Untungnya, Anda dapat dengan mudah melakukannya dengan menggunakan pendekatan Berbasis Konvensi dan / atau Berbasis Atribut (alias Perutean Atribut ).

Berbasis Konvensi

Di kelas Startup.cs Anda, ganti ini:

app.UseMvc();

dengan ini:

app.UseMvc(routes =>
            {
                // Route Sample A
                routes.MapRoute(
                    name: "RouteSampleA",
                    template: "MyOwnGet",
                    defaults: new { controller = "Items", action = "Get" }
                );
                // Route Sample B
                routes.MapRoute(
                    name: "RouteSampleB",
                    template: "MyOwnPost",
                    defaults: new { controller = "Items", action = "Post" }
                );
            });

Berbasis Atribut

Hal yang hebat tentang MVC6 adalah Anda juga dapat menentukan rute pada basis per-pengontrol dengan menghias Controllerkelas dan / atau Actionmetode dengan parameter templat RouteAttributedan / atau HttpGet/ yang sesuai HttpPost, seperti berikut ini:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;

namespace MyNamespace.Controllers
{
    [Route("api/[controller]")]
    public class ItemsController : Controller
    {
        // GET: api/items
        [HttpGet()]
        public IEnumerable<string> Get()
        {
            return GetLatestItems();
        }

        // GET: api/items/5
        [HttpGet("{num}")]
        public IEnumerable<string> Get(int num)
        {
            return GetLatestItems(5);
        }       

        // GET: api/items/GetLatestItems
        [HttpGet("GetLatestItems")]
        public IEnumerable<string> GetLatestItems()
        {
            return GetLatestItems(5);
        }

        // GET api/items/GetLatestItems/5
        [HttpGet("GetLatestItems/{num}")]
        public IEnumerable<string> GetLatestItems(int num)
        {
            return new string[] { "test", "test2" };
        }

        // POST: /api/items/PostSomething
        [HttpPost("PostSomething")]
        public IActionResult Post([FromBody]string someData)
        {
            return Content("OK, got it!");
        }
    }
}

Pengontrol ini akan menangani permintaan berikut:

 [GET] api/items
 [GET] api/items/5
 [GET] api/items/GetLatestItems
 [GET] api/items/GetLatestItems/5
 [POST] api/items/PostSomething

Perhatikan juga bahwa jika Anda menggunakan dua pendekatan togheter, rute berbasis atribut (ketika ditentukan) akan menimpa rute berbasis konvensi, dan keduanya akan menimpa rute default yang ditentukan oleh UseMvc().

Untuk info lebih lanjut bisa juga baca postingan berikut di blog saya.

Darkseal
sumber
1
Ini sempurna! Tidak ada jawaban lain yang benar-benar sesuai dengan kebutuhan saya. Tapi Anda menyelamatkan saya :)
King Arthur the Third
Apakah ada cara untuk menggunakan model yang telah ditentukan sebelumnya sebagai parameter kedua? Sebagai contoh, ketika saya menambal pengguna tertentu seperti ini: public IActionResult Patch(int id, [FromQuery] Person person), semua properti yang masuk adalah nol!
King Arthur the Third
0

Ubah saja WebAPIConfig.cs Anda seperti di bawah ini

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

Kemudian terapkan API Anda seperti di bawah ini

    // GET: api/Controller_Name/Show/1
    [ActionName("Show")]
    [HttpGet]
    public EventPlanner Id(int id){}
Dinuwan Kalubowila
sumber
0

Web APi 2 dan versi yang lebih baru mendukung jenis perutean baru, yang disebut perutean atribut. Sesuai dengan namanya, atribut routing menggunakan atribut untuk mendefinisikan rute. Perutean atribut memberi Anda lebih banyak kontrol atas URI di API web Anda. Misalnya, Anda bisa dengan mudah membuat URI yang mendeskripsikan hierarki resource.

Sebagai contoh:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }

Akan sempurna dan Anda tidak memerlukan kode tambahan misalnya di WebApiConfig.cs. Hanya saja Anda harus memastikan perutean api web aktif atau tidak di WebApiConfig.cs, jika tidak Anda dapat mengaktifkan seperti di bawah ini:

        // Web API routes
        config.MapHttpAttributeRoutes();

Anda tidak perlu melakukan sesuatu lebih atau mengubah sesuatu di WebApiConfig.cs. Untuk lebih jelasnya Anda dapat melihat artikel ini .

nzrytmn
sumber