Pido disculpas de antemano, esta publicación se aparta un poco de lo que me preguntaste, pero todo esto se hizo borroso cuando leí tu pregunta.
WebAPI a juego Semántica
El juego utilizado semántica por (los predeterminados rutas en) WebAPI es bastante simple.
- coincida con el nombre de la acción con el verbo (verbo = LLEGAR? Buscarse un nombre de método a partir de "GET")
- si se pasa un parámetro, la API busca una acción con un parámetro
Por lo tanto, en su muestra de código una solicitud GET sin un parámetro coincide con la función Get*()
sin parámetros. A Obtener contenido e ID busca un Get***(int id)
.
Ejemplos
Mientras que la semántica juego es simple, se crea una cierta confusión para los desarrolladores MVC (bueno, al menos este desarrollador). Veamos algunos ejemplos:
Nombres impares - Tu método get puede llamarse cualquier cosa, siempre que comience con "get". Por lo tanto, en el caso de un controlador de widgets, puede asignarle un nombre a sus funciones GetStrawberry()
y seguirá coincidiendo. Piense en el juego como algo parecido a: methodname.StartsWith("Get")
métodos de emparejamiento múltiples - ¿Qué pasa si usted tiene dos consigue métodos sin parámetros? GetStrawberry()
y GetOrange()
. Lo mejor que puedo decir es que la función definida primero (la parte superior del archivo) en tu código gana ... extraño. Esto tiene el efecto secundario de hacer que algunos métodos en su controlador sean inalcanzables (al menos con las rutas predeterminadas) ... extraño.
NOTA: la beta comportado como anteriormente para 'emparejan múltiples métodos' - La versión de lanzamiento RC & es un poco más OCD. Lanza un error si hay múltiples coincidencias potenciales. Este cambio elimina la confusión de múltiples coincidencias ambiguas. Al mismo tiempo, reduce nuestra capacidad para mezclar las interfaces de estilo REST y RPC en el mismo controlador, dependiendo de la orden de las rutas superpuestas &.
¿Qué hacer?
Bueno, WebAPI es nuevo y el consenso sigue vinculándose. La comunidad parece estar buscando los principios de REST bastante. Sin embargo, no todas las API pueden o deben ser RESTful, algunas se expresan de forma más natural en un estilo RPC. REST & lo que las personas llaman REST parece ser la fuente de quite a bitof confusion, wellat least toRoy Fielding.
Como pragmático, sospecho que muchas API serán 70% RESTful, con algunos métodos de estilo RPC. En primer lugar, la proliferación del controlador solo (dado el método de enlace webapi) va a volver loco a los desarrolladores. En segundo lugar, WebAPI en realidad no tiene una forma integrada de crear una estructura anidada de rutas api (lo que significa: /api/controller/
es fácil, pero /api/CATEGORY/Sub-Category/Controller
es factible, pero duele).
Desde mi perspectiva, me gustaría ver el control de la estructura de carpetas WebAPI los caminos API por defecto ... es decir, si se crea una carpeta de categoría en mi proyecto de interfaz de usuario a continuación, /api/Category
sería la ruta por defecto (algo parallel to this MVC article).
¿Qué hice?
Entonces, tenía algunos requisitos: (1) poder utilizar una sintaxis tranquila en la mayoría de los casos, (2) tener una separación de "espacio de nombres" de controladores (pensar en subcarpetas), (3) poder llamar métodos adicionales similares a rpc cuando sea necesario. La implementación de estos requisitos se redujo a un enrutamiento inteligente.
// SEE NOTE AT END ABOUT DataToken change from RC to RTM
Route r;
r = routes.MapHttpRoute(name : "Category1",
routeTemplate : "api/Category1/{controller}/{id}",
defaults : new { id = RouteParameter.Optional });
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category1"};
r = routes.MapHttpRoute(name : "Category2",
routeTemplate : "api/Category2/{controller}/{id}",
defaults : new { id = RouteParameter.Optional });
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category2"};
routes.MapHttpRoute( name : "ApiAllowingBL",
routeTemplate : "api/{controller}/{action}/{id}",
defaults : new { id = RouteParameter.Optional });
routes.MapHttpRoute( name : "DefaultApi",
routeTemplate : "api/{controller}/{id}",
defaults : new { id = RouteParameter.Optional });
- Las primeras dos rutas crean rutas "sub-carpeta". Necesito crear una ruta para cada subcarpeta, pero me limité a categorías principales, así que solo termino con 3-10 de estos. Observe cómo estas rutas agregan el token de datos
Namespace
, para restringir qué clases se buscan para una ruta en particular.Esto corresponde muy bien a la configuración de espacio de nombres típico cuando agrega carpetas a un proyecto de IU.
- La tercera ruta permite llamar a nombres de métodos específicos (como el mvc tradicional). Dado que la API web elimina el nombre de la acción en la URL, es relativamente fácil saber qué llamadas quieren esta ruta.
- La última entrada de ruta es la ruta de API web predeterminada. Esto capta cualquier clase, particularmente las que están fuera de mis 'subcarpetas'.
Dicho de otra manera
Mi solución descendió a bajar a los controladores que separan un poco más de lo que /api/XXXX
no esté demasiado concurrido.
- creo una carpeta en mi proyecto de interfaz de usuario (digamos
Category1
), y poner los controladores de la API dentro de la carpeta.
- Visual Studio naturalmente establece espacios de nombres de clase basados en la carpeta. Por lo tanto,
Widget1
en la carpeta Category1
obtiene un espacio de nombre predeterminado de UI.Category1.Widget1
.
- Naturalmente, quería que las URL de las API reflejaran la estructura de las carpetas (
/api/Category1/Widget
). La primera asignación que ve arriba logra que, mediante la codificación dura de /api/Category1
en la ruta, el token namespace
restringe las clases en las que se buscará un controlador que coincida.
NOTA: a partir de la liberación DataTokens
son nulos por defecto. No soy seguro si this is a bug, or a feature. Así que escribí un poco de método de ayuda y se añade a mi archivo RouteConfig.cs
....
r.AddRouteToken("Namespaces", new string[] {"UI.Controllers.Category1"});
private static Route AddRouteToken(this Route r, string key, string[] values) {
//change from RC to RTM ...datatokens is null
if (r.DataTokens == null) {
r.DataTokens = new RouteValueDictionary();
}
r.DataTokens[key] = values;
return r;
}
NOTA 2: incluso pensamos que este es un post WebAPI 1, como se señala en el @Jamie_Ide comentarios la solución anterior no funciona en WebAPI 2 porque IHttpRoute.DataTokens
no tiene setter. Para evitar esto se puede utilizar un método de extensión simple como esto:
private static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, string[] namespaceTokens)
{
HttpRouteValueDictionary defaultsDictionary = new HttpRouteValueDictionary(defaults);
HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints);
IDictionary<string, object> tokens = new Dictionary<string, object>();
tokens.Add("Namespaces", namespaceTokens);
IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: tokens, handler:null);
routes.Add(name, route);
return route;
}
En WebAPI I _think_ que se supone que tienen un controlador de recursos por lo que no tendría dos métodos de borrado. –
Gracias Shane, esa es una buena información, pero lo que quiero decir es específicamente como un método de registro único y un método de registro múltiple. GET es probablemente un ejemplo más apropiado, pero es posible que tenga un método que obtiene un registro por un ID, y otro que obtiene todos los registros con un cierto FK. Pero ambos usan el verbo GET; No entiendo dónde existe la inteligencia de enrutamiento que discierne entre estos. –