2015-09-10 11 views
5

Mam aplikację MVC z kilkoma prostymi stronami, które w większości będą działały w wywołaniach Web API. Dla uproszczenia chcę je uwzględnić w tym samym projekcie. Mogę uruchomić i nawigować po mojej stronie, ale kiedy próbuję wywołać moje API przez Ajax, ciągle otrzymuję błąd 404 - nie mogę znaleźć funkcji API.Nie można połączyć się z kontrolerem WebAPI za pośrednictwem AJAX, gdy jest on hostowany za pomocą MVC

Oto mój plik javascript:

$(document).ready(function() { 
    //set the login button to call our test API function 
    document.getElementById("login_submit").addEventListener("click", GetUser); 

}); 

function GetUser() { 
    var response = $.ajax({   
     url: '/api/User', 
     method: 'GET', 
     contentType: 'application/json; charset=utf-8', 
     success: function (data) { 
      alert("Success!"); 
     }, 
     error: function (request, status, error) { 
      alert(error); 
     } 
    }); 
} 

I tu jest mój kontroler:

namespace MyProject.Controllers.API 
{ 
    public class UserController : ApiController 
    { 
     // GET api/<controller> 
     [HttpGet]   
     public IEnumerable<string> Get() 
     { 
      return new string[] { "value1", "value2" }; 
     } 

     // GET api/<controller>/5 
     [HttpGet]   
     public string Get(int id) 
     { 
      return "value"; 
     } 
    } 
} 

API Kontrolery są w ich własnym folderze (zwany "API") w moim folderze Controllers w moim projekt - dlatego przestrzeń nazw zawiera "API" na tym przykładowym kontrolerze.

Gdy używam F12 w przeglądarce, aby uchwycić żądania coraz wysłany, widzę, że moja rozmowa ma następującą informację:

Request URL: http://localhost:50035/api/User 
Request Method: GET 
Status Code: 404/Not Found 

Teraz moje zrozumienie jest, że powinno to znaleźć API o nazwie UserController i znajdź funkcję za pomocą znacznika [HttpGet] bez żadnych argumentów, a następnie zwróć tablicę ciągów znaków ("wartość1", "wartość2"). Zamiast tego niczego nie znajduje.

Jako ostatnia uwaga, tu jest mój config routingu (i tak, to jest inicjowany na Global.asax):

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

      // Web API routes 
      config.MapHttpAttributeRoutes(); 

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

UPDATE:

Na podstawie informacji zwrotnych mam dotychczas otrzymałem moją konfigurację Global.asax. It teraz wygląda następująco:

protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     GlobalConfiguration.Configure(WebApiConfig.Register); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     BundleConfig.RegisterBundles(BundleTable.Bundles); 

    } 

Teraz gdy zgłoszę mojej funkcji API, mam ... w toku. Nie zwraca komunikatu o powodzeniu. Po prostu wisi. Nie dostaję "sukcesu!" alarm.

+2

Jaka jest kolejność rejestrowania tras w metodzie Global.asax Application_Start()? JEŚLI rejestrujesz trasy MVC przed rundami WebAPI, wtedy trasy APi zakończą się niepowodzeniem! –

+2

Powielono problem, rejestrując trasy MVC _przed trasami WebApi. –

+0

Jeśli umieścisz punkt przerwania w metodzie Get() kontrolera użytkownika, czy zostanie on trafiony? –

Odpowiedz

2

Chociaż nie jestem pewien, czy to ten sam problem, który zgadłem w moim komentarzu, ale jest to prawdopodobny powód. Więc każdy, kto ma podobny problem, to jest problem.

Silnik trasowania MVC próbuje dopasować przychodzące żądania do tras w tej samej kolejności, w jakiej zostały zarejestrowane.

  1. Tak więc, jeśli zarejestrować pierwszej trasy MVC - {kontroler}/{działania}/{id} id: opcjonalnie
  2. A następnie zarejestrować trasę WebAPI - api/{kontroler}/{ id} id: opcjonalnie

następnie przychodzące żądania zostaną dopasowane trasie MVC i jeśli nie pasuje do wzorca trasy, tylko wtedy będzie ona porównywana z trasy WebAPI.

Teraz, jeśli masz takie żądanie, jak api/User, będzie ono pasowało do wzorca trasy MVC i NIE będzie porównywane z trasą WebAPI. W rezultacie MvcHandler spróbuje stworzyć klasę kontrolera MVC ApiController i wywołać na niej metodę User().W rezultacie klient otrzyma 404 - nie znaleziono zasobu!

Dodatkowo, jeśli nie używasz routingu atrybutu, może chcesz usunąć/skomentować ten wiersz

//config.MapHttpAttributeRoutes(); 

I bezpieczniej http-czasownik-do-API-metody routingu, można dodać następujące w twoja konfiguracja api.

routes.MapHttpRoute("RestApiRoute", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" }); //this replaces your current api route 
routes.MapHttpRoute("ApiWithActionRoute", "Api/{controller}/{action}/{id}", new { id = RouteParameter.Optional }); 
routes.MapHttpRoute("DefaultApiGetRoute", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(new string[] { "GET" }) }); 
routes.MapHttpRoute("DefaultApiPostRoute", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(new string[] { "POST" }) }); 
routes.MapHttpRoute("DefaultApiPutRoute", "Api/{controller}", new { action = "Put" }, new { httpMethod = new HttpMethodConstraint(new string[] { "PUT" }) }); 
routes.MapHttpRoute("DefaultApiDeleteRoute", "Api/{controller}", new { action = "Delete" }, new { httpMethod = new HttpMethodConstraint(new string[] { "DELETE" }) }); 

Pierwsze dwie trasy pozwalają nazwać punkty końcowe w [1] czystej REST sposób & do [2] nazywają je z nazwą metody (nie dostosowane do standardów REST chociaż!)

+0

Dziękuję - to było bardzo pomocne. Przeniosłem kod, a teraz mogę przejść przez początkowy błąd, który dostaję, ale nadal nie otrzymuję mojego "sukcesu!" alarm. Zobacz moją edycję, aby uzyskać więcej informacji. – Max

+0

@ user2291983 Nie widzę WebApiConfig.Register() w Twojej metody uruchamiania aplikacji, czy brakuje mi czegoś? Czy możesz po prostu nacisnąć http: // localhost: 50035/api/User (zmienić numer portu, jeśli to się zmieniło) z paska adresu przeglądarki i zobaczyć, co się stanie? –

+1

WebApiConfig.Register znajduje się w wywołaniu GlobalConfiguration.Configure(). Szczerze mówiąc, nie wiem, co to robi, ale wszystkie moje książki tak to zrobiły. Wywołanie adresu URL działało zgodnie z oczekiwaniami - otrzymałem obiekt JSON z powrotem zawierający tablicę ciągów znaków. – Max

1

Spróbuj

/api/User/Get 

zamiast just/api/user. Jeśli chcesz się z tym ubrać, sprawdź numer [RoutePrefix] and [Route], aby móc zarządzać tymi rzeczami ręcznie.

+1

To działało, ale jest sprzeczne z tym, co myślałem, że rozumiem o wywołaniach API. Użytkownik jest nazwą kontrolera i próbuję wywołać funkcję GET bez parametrów przy użyciu żądania HTTP Get. – Max

+0

@ user2291983 oznaczyć to jako prawidłową odpowiedź? :) Wiem, MS nie jest tak gorąca w wyjaśnianiu tego, szczególnie, gdy pokazuje domyślną architekturę routingu. Tak po prostu jest. . ./{controllerName}/{actionName}/{parametr} lub /? parametr = XXX. Gdy zaczniesz tworzyć niestandardowe interfejsy API (o nazwach innych niż "GET" i "POST"), będzie to miało więcej sensu. – codeMonkey