Kilka postów wcześniej pisałem o JSONP, jako sposobie na wywoływanie serwisów znajdujących się w innych domenach z poziomu JavaScript. Domyślnie przeglądarki blokują takie wywołania ze względu na bezpieczeństwo. Załóżmy, że mamy następujący serwis w jakiejś domenie:
public class ValuesController : ApiController { // GET api/values/5 public string Get() { return "Hello World"; } }
Następnie w drugiej domenie mamy kod JavaScript próbujący pobrać dane z powyższej usługi:
$.ajax({ url: "http://localhost:24523/api/Values" }). done(function (data) { $("#testLabel").text(data.Text); });
Próba połączenia się z usługą zakończy się oczywiście następującym błędem:
“XMLHttpRequest cannot load http://localhost:24523/api/Values.
No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:24018’ is therefore not allowed access.”
CORS to standard zaimplementowany przez większość współczesnych przeglądarek internetowych. W skrócie jeśli kod JavaScript chce wykonać zapytanie cross-domain to przeglądarka najpierw wyśle specjalny pakiet do usługi. Jeśli usługa wyrazi zgodę na cross-domain, wtedy połączenie między domenowe zostanie nawiązane. Innymi słowy, to serwer decyduje czy dopuścić dane połączenie z obcej domeny. Z punktu technicznego zatem, zarówno przeglądarka jak i serwer muszą wspierać CORS. W WebAPI bardzo prostą możemy zaimplementować CORS. Wystarczy, że zainstalujemy następujący pakiet:
Install-Package Microsoft.AspNet.WebApi.Cors
Następnie w WebConfig wywołujemy EnableCors:
public static void Register(HttpConfiguration config) { // Web API configuration and services config.EnableCors(); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }
Należy również oznaczyć dany kontroler atrybutem EnableCors:
[EnableCors( "http://localhost:24018","*","*")] public class ValuesController : ApiController { // GET api/values/5 public string Get() { return "Hello World@"; } }
Po uruchomieniu strony, wszystko załaduje się prawidłowo. Jak widać jest to trochę prostsze niż JSONP i bardziej naturalne. JSONP to tak naprawdę wykorzystywanie pewnej luki w przeglądarkach, aczkolwiek jest to bardzo powszechna praktyka i nic nie stoi na przeszkodzie, aby po prostu używać JSONP.
Zajrzyjmy jeszcze do definicji atrybutu EnableCors:
public EnableCorsAttribute(string origins, string headers, string methods) : this(origins, headers, methods, (string) null) { }
Jak widać, najważniejszy parametr to origin czyli adres strony, która będzie wywoływała daną usługę. Innymi słowy, w powyższym rozwiązaniu akceptujemy wyłącznie domenę http://localhost:24523. Możemy również być bardziej wybredni co do przychodzących zapytań i określić konkretne nagłówki czy metody HTTP (GET\POST itp.).
Przyjrzyjmy się również pakietom jakie przeglądarka i usługa wysyłają. W momencie, gdy klient (przeglądarka) próbuje nawiązać połączenie między domenowe, przeglądarka wyśle pakiet z nagłówkiem Origin równym adresowi klienta czyli w tym przypadku “Origin: http://localhost:24018”:
Accept:*/* Accept-Encoding:gzip, deflate, sdch Accept-Language:en-GB,en;q=0.8,en-US;q=0.6,pl;q=0.4 Cache-Control:max-age=0 Connection:keep-alive Host:localhost:24523 Origin:http://localhost:24018 Referer:http://localhost:24018/Home/Index User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36
Jeśli domena pokryje się z regułami opisanymi za pomocą EnableCors, wtedy zostanie zwrócony pakiet z nagłówkiem Access-Control-Allow-Origin równym danej domenie tzn.:
Access-Control-Allow-Origin:http://localhost:24018 Cache-Control:no-cache Content-Length:14 Content-Type:application/json; charset=utf-8 Date:Mon, 03 Aug 2015 18:09:57 GMT Expires:-1 Pragma:no-cache Server:Microsoft-IIS/10.0 X-AspNet-Version:4.0.30319 X-Powered-By:ASP.NET X-SourceFiles:=?UTF-8?B?YzpcdXNlcnNccGlvdHJ6XGRvY3VtZW50c1x2aXN1YWwgc3R1ZGlvIDIwMTVcUHJvamVjdHNcV2ViQXBwbGljYXRpb24yXFdlYkFwcGxpY2F0aW9uMlxhcGlcVmFsdWVz?=
W przyszłym poście opiszę jeszcze kilka rzeczy związanych z CORS. Na zakończenie zachęcam na zapoznanie się, które przeglądarki posiadają wsparcie dla CORS: http://caniuse.com/#feat=cors