2013-01-21 15 views
8

Wygląda na to, że istnieje wbudowana domyślna logika dla interfejsu Web API, która używa nazwy czasownika HTTP jako nazwy akcji, jeśli w adresie URL nie podano żadnych akcji. Na przykład, mam tę trasę:Jak zmodyfikować domyślne trasy Web API szkieletu?

 config.Routes.MapHttpRoute(
      name: "DefaultApiController", 
      routeTemplate: "api/{controller}" 
     ); 

A oto moje działania:

public IEnumerable<Conference> Get() 
    { 
     ... 
    } 

    [ActionName("current")] 
    public IEnumerable<Conference> GetCurrent() 
    { 
     ... 
    } 

Kiedy idę do ~/Konferencje z czasownikiem GET, to zajmie Ci „Get()" akcja. Jeśli użyjesz czasownika POST, przeniesie Cię on do akcji "Post ([FromBody] value") ... i tak dalej. Istnieje konflikt chociaż podczas próby, aby przejść do ~/Konferencje/GetCurrent (chociaż mam [ActionName („bieżącej”)] na górze):

Wiele działań stwierdzono, że pasuje do żądania: Systemu .Collections.Generic.IEnumerable 1[MyApp.Models.Conference] Get() on type MyApp.Api.ConferencesController System.Collections.Generic.IEnumerable 1 [MyApp.Models.Conference] GetCurrent() od typu MyApp.Api.ConferencesController

oznacza to, że za pomocą ramy startsWith zamiast równą określenie domyślnego działania. Ignoruje także atrybut ActionName przy dopasowywaniu czasownika do działania.

Moje pytanie brzmi: jak ustawić domyślną akcję ramki, aby dopasować dokładnie do czasownika, zamiast używać logiki StartsWith? Czasownik GET powinien pasować tylko do akcji Get(), a nie Get(), GetCurrent() GetPast(), itp. (Szczególnie gdy ignoruje atrybut ActionName).

EDYTOWANIE Dla uproszczenia, pokazałem tylko jedną z moich tras powyżej. Myślę, że może to pomóc, jeśli pokażę wszystkie moje trasy, które wciąż są w fazie roboczej. Staram się uzyskać w pełni funkcjonalny API REST jednocześnie pozostawiając miejsce na dodanie moje własne działania niestandardowe:

public static void Register(HttpConfiguration config) 
    { 
     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerActionId", 
      routeTemplate: "api/{controller}/{action}/{id}", 
      defaults: null, 
      constraints: new { action = @"^[a-zA-Z]+$", id = @"^\d+$" } // action must start with character 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerActionName", 
      routeTemplate: "api/{controller}/{action}/{name}", 
      defaults: null, 
      constraints: new { action = @"^[a-zA-Z]+$", name = @"^[a-zA-Z]+$" } // action and name must start with character 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerId", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: null, 
      constraints: new { id = @"^\d+$" } // id must be all digits 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerAction", 
      routeTemplate: "api/{controller}/{action}", 
      defaults: null, 
      constraints: new { action = @"^[a-zA-Z]+$" } // action must start with character 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiController", 
      routeTemplate: "api/{controller}" 
     ); 

UPDATE Wydaje się, że dodawanie contraints czasownik HTTP pomógł:

 config.Routes.MapHttpRoute(
      name: "DefaultApiControllerGet", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Get" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) } 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerPost", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Post" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) } 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerPut", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Put" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Put) } 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerDelete", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Delete" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Delete) } 
     ); 

Odpowiedz

7

EDIT : Po wprowadzeniu dużej zmiany w pytaniu muszę zmienić odpowiedź:

Krótko mówiąc - to nigdy nie zadziała po wyjęciu z pudełka API, ponieważ będzie on domyślnie wysyłają działania:

  1. na podstawie nazwy działania, jeśli {akcja} jest częścią danych trasy
  2. podstawie czasownika HTTP

Jednak te dwa podejścia nie można mieszać w jednym kontrolerze, więc nie będziesz w stanie wysyłać akcji za pomocą obu podejść z jednego kontrolera (co właśnie próbujesz zrobić).

Masz trzy sposoby, aby rozwiązać ten problem:

  1. przerobienie swoich zasobów, dzięki czemu masz osobne kodeków do działania nazwa-dyspozytorskich oraz disptaching czasownika oparte (co jest dalekie od ideału)

  2. Zarejestruj trasy ręcznie dla każdej z zagnieżdżonych tras. W ten sposób wysyłamy wiadomości przez czasownik HTTP, ale routing wyraźnie wskazuje na konkretne działanie. Aby to uprościć, możesz użyć czegoś takiego jak AttributeRouting (https://github.com/mccalltd/AttributeRouting). Minusem jest oczywiście - efektywnie - jedna trasa na akcję

  3. Wdrożyć nowy IActionSelector, który pozwoliłby mieszać zarówno dyspozycje oparte na czasownikach, jak i na nazwach w jednym kontrolerze. Jest to najbardziej "nisko-poziomowe" rozwiązanie, ale wygląda dokładnie tak, jak chcesz. Zamieściłem instruktażu w ubiegłym tygodniu - http://www.strathweb.com/2013/01/magical-web-api-action-selector-http-verb-and-action-name-dispatching-in-a-single-controller/

+0

na odpoczynek API, bym nadal trzeba uzyskać ~/Konferencje nadal działa chociaż – Basem

+0

EDIT: OK, teraz, kiedy pisał wszystkie swoje trasy, obraz jest znacznie jaśniejszy;) –

+0

Niesamowity artykuł, thx! Również dźwięki AttributeRouting są obiecujące, ale wygląda na to, że wydajność Web Cache i funkcje buforowania nie są obsługiwane. – Basem