Skoro już zaprzyjaźniłem się z Aurelią to warto byłoby stworzyć WebApi do mojej aplikacji, które to aplikacja stworzona za pomocą wyżej wspomnianej Aurelii będzie z radością konsumować. Wybrałem podejście WebApi, bo:
- WebApi2, czyli .Net Core nie chciał działa z Autofac’iem. Próbowałem podpiąć Autofac po zainstalowaniu Update 3 dla Visual Studio 2015 i niestety dostawałem błąd z klasy Microsoft.Extensions.DependencyInjection. Wyjątek, a i owszem zapisałem, ale tak dobrze, że nie mogę go znaleźć. Jeżeli uda mi się go jeszcze raz wywołać to uzupełnię to miejsce.
- NancyFX, a i owszem chciałem, ale okazało się, że nie ma szablonu dla VS 2015. Nie to nie, chociaż można oczywiście użyć Nancy bez szablonu.
A więc pozostało mi stare WebApi i pomyślałem „dlaczego nie?”. W sumie w nim też nigdy nic nie napisałem.
Do dzieła. Wybrałem projekt „ASP.NET Web Application (.NET Framework)”, a następnie zaznaczyłem „Empty”, a potem wybrałem „WebApi”. Mam to. Kolejna sprawa to CORS, bo jest duże prawdopodobieństwo, że frontendowa część aplikacji będzie pochodzić z innej domeny czy też będzie dostępna na innym porcie niż część serwerowa. A właściwie to już tak jest, bo development też wymusza użycie różnych portów.
Cross-Origin Resource Sharing to, jak mówi sama Wikipedia, mechanizm umożliwiający współdzielenie zasobów pomiędzy serwerami znajdującymi się w różnych domenach. Ściślej rzecz biorąc chodzi o możliwość wykonywania żądań AJAX między takimi serwerami przy zachowaniu pewnych ograniczeń co do dopuszczalnego źródła żądania.
Aby umożliwić CORS wystarczy dodać do projektu referencję System.Web.Http.Cors z nugetowego pakietu Microsoft.AspNet.WebApi.Cors, następnie w klasie WebApiConfig.cs (metoda Register) wstawić następujący kawałek kodu:
var cors = new EnableCorsAttribute("http://localhost:9000", "*", "*"); config.EnableCors(cors);
Działa, chociaż nie od razu działało. Zupełnie przypadkiem zacząłem całą drogę naokoło. Najpierw oprócz powyższego wpisu dodawałem jeszcze różnej maści wpisy w web.configu:
Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS Access-Control-Allow-Origin: *
Następnie dekorowałem atrybutem EnableCors każdy kontroler mojego Api. Nie. Wystarczy po prostu to co jest powyżej.
I jeszcze jedno. Gdyby ktoś dostał błąd:
Fetch API cannot load http://localhost:49911/api/ohdev. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. The response had HTTP status code 500. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
to może spróbować od usunięcia ostatniego slasha z adresu podanego jako parametr konstruktora EnableCorsAttribute – oczywiście jeżeli ten slash tam się znajduje.
http://localhost:9000 - dobrze http://localhost:9000/ - źle
OK, czas na Autofac, czyli kontener, który wstrzyknie wszystko tam gdzie trzeba i nie będziemy musieli się martwić o zależności. Dlaczego Autofac? Bo koledzy go używają, a ja ich bardzo cenię i nie zamierzam się mądrzyć.
Zaczynam od dwóch nugetowych paczek Autofac i Autofac.WebApi oraz wstawienia kodu prosto z dokumentacji Autofac:
protected void Application_Start() { var builder = new ContainerBuilder(); var config = GlobalConfiguration.Configuration; builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); var container = builder.Build(); config.DependencyResolver = new AutofacWebApiDependencyResolver(container); GlobalConfiguration.Configure(WebApiConfig.Register); }
i dostaję w twarz pięknym błędem:
Nie można załadować pliku lub zestawu 'Autofac' lub jednej z jego zależności.
Okazało się, że najlepiej zainstalować tylko jedną paczkę: Autofac.WebApi, a podczas instalacji zostanie już doinstalowany Autofac we właściwej wersji. Ja miałem rozbieżności i dostawałem błąd.
Jedziemy dalej. Kolejne F5 i:
Naruszono reguły zabezpieczeń dziedziczenia podczas zastępowania elementu członkowskiego: Autofac.Integration.WebApi.AutofacWebApiDependencyResolver.BeginScope(). Dostępność zabezpieczeń metody zastępującej musi być taka sama jak dostępność zabezpieczeń metody zastępowanej.
W końcu doczytałem na jednym z wpisów na StackOverflow, że dla ostatnich klasycznych WebApi należy stosować Autofac.WebApi2. Nie doczytałem, a szczerze powiedziawszy to nadal nie mam pewności czy to jest tam napisane. Teraz to jest nieważne. Po dwóch wieczorach nierównej walki … BANGLA.
Jeszcze tylko rejestracja jednej klasy z jednym parametrem w konstruktorze:
builder.RegisterType<DbFactory>().As<IDbFactory>().WithParameter("connectionString", ConfigurationManager.ConnectionStrings["ohdevConnectionString"].ConnectionString);
Do tego rejestracja generycznego repozytorium:
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
A na koniec rejestracja wszystkich typów w projekcie, w którym znajduje się klasa OhDev:
var domainAssembly = typeof (OhDev).Assembly; builder.RegisterAssemblyModules(domainAssembly);
Działa!!!