Nombres de métodos personalizados en ASP.NET Web API

Estoy convirtiendo de WCF Web API a la nueva ASP.NET MVC 4 Web API. Tengo un UsersController, y quiero tener un método llamado Authenticate. Veo ejemplos de cómo hacer GetAll, GetOne, Publicar y Eliminar, sin embargo, ¿qué sucede si quiero agregar métodos adicionales a estos servicios? Por ejemplo, mi UsersService debe tener un método llamado Autenticar donde pasan un nombre de usuario y contraseña, sin embargo, no funciona.

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

Puedo navegar a myapi / api / users / y se llamará GetAll y puedo navegar a myapi / api / users / 1 y se llamará a Get, pero si llamo a myapi / api / users / authenticate? Username = {0} & password = {1} luego llamará a Get (NO autenticación) y error:

El diccionario de parámetros contiene una entrada nula para el parámetro ‘id’ de tipo no nulo ‘System.Int32’ para el método ‘System.String Get (Int32)’ en ‘Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController’. Un parámetro opcional debe ser un tipo de referencia, un tipo anulable o ser declarado como un parámetro opcional.

¿Cómo puedo llamar a nombres de métodos personalizados como Autenticar?

Por defecto, la configuración de la ruta sigue las convenciones RESTFul, lo que significa que solo aceptará los nombres de las acciones Obtener, Publicar, Poner y Eliminar (mire la ruta en global.asax => por defecto no le permite especificar ningún nombre de acción => usa el verbo HTTP para despachar). Entonces, cuando envías una solicitud GET a /api/users/authenticate , básicamente llamas a la acción Get(int id) y pasas id=authenticate que obviamente se bloquea porque tu acción Get espera un entero.

Si desea tener diferentes nombres de acción que los estándar, puede modificar su definición de ruta en global.asax :

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

Ahora puede navegar a /api/values/getauthenticate para autenticar al usuario.

Este es el mejor método que he encontrado hasta ahora para incorporar métodos GET adicionales mientras apoyo los métodos normales de REST también. Agregue las siguientes rutas a su WebApiConfig:

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

Verifiqué esta solución con la clase de prueba a continuación. Pude golpear exitosamente cada método en mi controlador a continuación:

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

Verifiqué que admite las siguientes solicitudes:

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

Tenga en cuenta que si sus acciones GET adicionales no comienzan con ‘Obtener’, puede agregar un atributo HttpGet al método.

Estoy días en el mundo de MVC4.

Por lo que vale, tengo un AdsAPIController, y necesitaba un método personalizado, que se podría llamar así:

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

Con diferentes valores para el último parámetro para obtener registros con diferentes disposiciones.

Lo que finalmente funcionó para mí fue:

El método en el sitioAPIController:

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

Y esto en el 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}" ); 

Mientras estaba nombrando la {disposition} como {id} me encontraba:

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

Cuando lo renombré a {disposición}, comenzó a funcionar. Aparentemente, el nombre del parámetro coincide con el valor en el marcador de posición.

No dude en editar esta respuesta para que sea más precisa / explicativa.

Web Api espera de forma predeterminada una URL en forma de api / {controller} / {id}, para anular este enrutamiento predeterminado. puede establecer el enrutamiento con cualquiera de las siguientes dos formas.

Primera opción:

Agregue el registro de rutas a continuación en WebApiConfig.cs

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

Decora tu método de acción con HttpGet y los parámetros como a continuación

 [HttpGet] public HttpResponseMessage ReadMyData(string param1, string param2, string param3) { // your code here } 

para llamar a la URL del método anterior será como a continuación

http: // localhost: [yourport] / api / MyData / ReadMyData? param1 = value1 & param2 = value2 & param3 = value3

Segunda opción Agregue prefijo de ruta a la clase de controlador y decore su método de acción con HttpGet como se muestra a continuación. En este caso, no es necesario cambiar ningún WebApiConfig.cs. Puede tener enrutamiento predeterminado.

 [RoutePrefix("api/{controller}/{action}")] public class MyDataController : ApiController { [HttpGet] public HttpResponseMessage ReadMyData(string param1, string param2, string param3) { // your code here } } 

para llamar a la URL del método anterior será como a continuación

http: // localhost: [yourport] / api / MyData / ReadMyData? param1 = value1 & param2 = value2 & param3 = value3

Vea este artículo para una discusión más larga de las acciones nombradas. También muestra que puede usar el atributo [HttpGet] en lugar de ponerle un prefijo al nombre de la acción con “get”.

http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

En caso de que esté utilizando ASP.NET 5 con ASP.NET MVC 6 , la mayoría de estas respuestas simplemente no funcionarán porque normalmente permitirá que MVC cree la colección de rutas apropiada para usted (utilizando las convenciones RESTful predeterminadas), lo que significa que no encontrará ninguna llamada Routes.MapRoute() para editar a voluntad.

El método ConfigureServices() invocado por el archivo Startup.cs registrará MVC con el marco de Dependency Injection integrado en ASP.NET 5: de ese modo, cuando llame a ApplicationBuilder.UseMvc() más adelante en esa clase, el framework MVC agregará automáticamente estos rutas predeterminadas a su aplicación. Podemos echar un vistazo a lo que sucede detrás del capó viendo la implementación del método UseMvc() dentro del código fuente del marco:

 public static IApplicationBuilder UseMvc( [NotNull] this IApplicationBuilder app, [NotNull] Action 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()); } 

Lo bueno de esto es que el framework ahora maneja todo el trabajo duro, iterando a través de todas las Acciones del Controlador y configurando sus rutas predeterminadas, ahorrándole así un poco de trabajo redundante.

Lo malo es que hay poca o ninguna documentación sobre cómo puede agregar sus propias rutas. Afortunadamente, puede hacerlo fácilmente utilizando un enfoque basado en la Convención y / o un enfoque basado en atributos (también conocido como Enrutamiento de atributos ).

Basado en la Convención

En su clase Startup.cs, reemplace esto:

 app.UseMvc(); 

con este:

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

Basado en atributos

Una gran ventaja de MVC6 es que también puede definir rutas por cada controlador decorando la clase Controller y / o los métodos de Action con los RouteAttribute apropiados de la RouteAttribute y / o HttpGet / HttpPost , como los siguientes:

 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 Get() { return GetLatestItems(); } // GET: api/items/5 [HttpGet("{num}")] public IEnumerable Get(int num) { return GetLatestItems(5); } // GET: api/items/GetLatestItems [HttpGet("GetLatestItems")] public IEnumerable GetLatestItems() { return GetLatestItems(5); } // GET api/items/GetLatestItems/5 [HttpGet("GetLatestItems/{num}")] public IEnumerable 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!"); } } } 

Este controlador manejará las siguientes solicitudes:

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

También tenga en cuenta que si utiliza los dos enfoques juntos, las rutas basadas en atributos (cuando se definan) anularían las basadas en la Convención, y ambas anularían las rutas predeterminadas definidas por UseMvc() .

Para obtener más información, también puede leer la siguiente publicación .