Category Archives: ASP .NET

LESS: rozszerzenia

Dzisiaj kolejne udogodnienie w CSS dostarczane przez LESS. W klasycznym CSS nie ma możliwości dziedziczenia klas. Jeśli chcemy, aby klasa B miała właściwości klasy A oraz kilka rozszerzających wtedy zwykle musimy duplikować kod.
W LESS rozszerzenia definiuje się następująco:

.childClass {
  &:extend(.baseClass);
  background: blue;
}

.baseClass {
  color: red;
}

Na wyjściu mamy:

.childClass {
  background: blue;
}
.baseClass,
.childClass {
  color: red;
}

Inna składnia to:

.childClass {
  background: blue;
}

.baseClass {
  color: red;
}

.childClass:extend(.baseClass){}

Więcej informacji znajduje się w dokumentacji. Na blogu zwykle planuję tylko umieszczać kilka przykładów jako wprowadzenie.

LESS: Mixins

Dzisiaj kolejna przydatna konstrukcja LESS. Często zachodzi potrzeba stworzenia klasy na podstawie kilku innych klas. Załóżmy, że mamy:

.class1
{
  color:red;
}
.class2
{
  font-size:10px;
}
.finalClass
{
  color:red;
  font-size:10px;
  
  border-color:blue;
}

FinalClass składa się zarówno z class1 jak i class2. Ponadto rozszerza możliwości o border-color. Za pomocą mixin możemy kod skrócić do:

.class1
{
  color:red;
}
.class2
{
  font-size:10px;
}
.finalClass
{
  .class1();
  .class2();
  
  border-color:blue;
}

Oczywiście w praktyce przydatne jest to, gdy klasy mają więcej właściwości niż jedna. W przypadku jednej właściwości lepiej skorzystać ze zmiennych.

Powyższy mixin wykona się do wspomnianych wcześniej stylów:

.class1 {
  color: red;
}
.class2 {
  font-size: 10px;
}
.finalClass {
  color: red;
  font-size: 10px;
  border-color: blue;
}

Możliwe jest również zadeklarowanie mixin za pomocą nawiasów ():

.class1()
{
  color:red;
}
.class2()
{
  font-size:10px;
}
.finalClass
{
  .class1();
  .class2();
  
  border-color:blue;
}

Różnica polega na tym, że na wyjściu nie pojawią się .class1\.class2:

.finalClass {
  color: red;
  font-size: 10px;
  border-color: blue;
}

Do mixin można przekazać parametry:

.class1(@color)
{
  color:@color;
}

.finalClass
{
  .class1(blue);
    
  border-color:blue;
}

Analogicznie, parametry mogą przyjmować domyślne wartości:

.class1(@color:green)
{
  color:@color;
}

.finalClass
{
  .class1();
    
  border-color:blue;
}

Należy pamiętać, że jeśli chce się przekazać kilka parametrów należy je oddzielić średnikiem tzn. @color;@color2 itd.

Tak jak w C#, parametry można przekazywać za pomocą nazwy czyli zamiast .class(red) można .class(@color:red). Do dyspozycji mamy również specjalny parametr o nazwie @arguments, który po prostu zawiera listę wszystkich argumentów:

.class1(@width;@style;@color)
{
  border:@arguments;
}

.finalClass
{
  .class1(@width:2px;@style:solid;@color:red);
    
  border-color:blue;
}

Czasami zachodzi potrzeba odpalenia różnych mixin w zależności od kontekstu, np. używanego urządzenia. Spójrzmy na następujący kod:

.class1(mobile)
{
  font-size:1px;
}
.class1(desktop)
{
  font-size:2px;
}


.finalClass
{
  .class1(mobile);
    
  border-color:blue;
}

Zdefiniowaliśmy tutaj różne mixin w zależności od typu urządzenia. Przekazując jako parametr mobile, ustawimy rozmiar czcionki na 1. Na wyjściu pojawi się zatem:

.finalClass {
  font-size: 20px;
  font-size: 1px;
  border-color: blue;
}

Jeśli zadeklarujemy pierwszy parametr jako zmienną wtedy uzyskamy generyczną klasę:

.class1(@device)
{
  border-width:4px;
}

.class1(mobile)
{
  font-size:1px;
}
.class1(desktop)
{
  font-size:2px;
}

.finalClass
{
  .class1(mobile);
    
  border-color:blue;
}

W powyższym przypadku “mobile” zostanie dopasowany do dwóch klas:

.finalClass {
  border-width: 4px;
  font-size: 1px;
  border-color: blue;
}

Jak widzimy, mixins mogą w znaczącym stopniu skrócić arkusze CSS poprzez unikanie duplikacji kodu.

LESS css – wprowadzenie, zmienne

Na blogu rzadko poruszam tematy CSS, ale czasami nawet jak ma się bardzo małą warstwę prezentacji, warto zainwestować trochę czasu w poznanie nowych bibliotek.

Dzisiaj chciałbym przedstawić LessCss.  Biblioteka stanowi pewnego rodzaju pre-processor, który rozszerza możliwości klasycznego CSS. Na wyjściu zatem pojawi się zwykły CSS, który jest obsługiwany przez wszystkie przeglądarki. Sprawą “kompilacji”, zajmiemy się w innym wpisie, ponieważ istnieje wiele sposobów. Jednym z nich jest np. kompilowanie za pomocą  JavaScript po stronie klienta. W tym poście będę korzystał z narzędzia online, które można znaleźć tutaj.

Załóżmy, że mamy kilka klas, które korzystają z tego samego koloru:


.customClass1
{
  color:red;
}

.customClass2
{
  color:red;
}

.customClass3
{
  color:red;
}

Za pomocą LESS możemy zdefiniować zmienną:

@customColor: red;

.customClass1
{
  color:@customColor;
}

.customClass2
{
  color:@customColor;
}

.customClass3
{
  color:@customColor;
}

Po w klejeniu wspomniany wcześniej edytor online, zobaczymy, że na wyjściu @customColor zostanie zastąpiona wartością Red. Wszelkie zmienne w Less definiujemy za pomocą @. Zmiana wartości odbywa się za pomocą znaku ‘:’ (dwukropek).

Wszelkie stałe zatem można w łatwy sposób umieścić tylko w jednym miejscu. Zmienne nie ograniczają się jedynie do wartości. Możliwe jest również umieszczenie selektorów CSS w nich, tzn.:

@customSelector: .customClass;

@{customSelector}
{
  color:red;
}

Po kompilacji:

.customClass {
  color: red;
}

Zmienna ładowane są w sposób “lazy-loading. W praktyce oznacza to, że nie muszą być deklarowane przed ich użyciem. Poniższy kod również wygeneruje taką samą odpowiedź:

@{customSelector}
{
  color:red;
}

@customSelector: .customClass;

Osobiście uważam, że może z tego wyniknąć wiele problemów.

W dokumentacji znalazłem również informacje, że możliwe jest korzystanie ze zmiennej jako nazwy dla kolejnej zmiennej. Spójrzmy na następujący kod:

@varName: 'test';
@test: 'red';

.customClass
{
  color:@@varName
}

Po kompilacji:

.customClass {
  color: 'red';
}

Moim zdaniem bardzo niebezpieczna konstrukcja. Podwójny operator @ wyciągnie najpierw wartość ze zmiennej varName, a potem użyje jej jako docelowej zmiennej.

W następnych wpisach skupie się na pozostałych możliwościach LESS oraz na kompilacji LESS do normalnego CSS.

ASP.NET Core – wstrzykiwanie zależności

ASP.NET Core ułatwia w znaczący sposób IoC. Przede wszystkim wbudowano w framework dosyć prostą implementację IoC. Oznacza to, że w wielu przypadkach nie trzeba już instalować zewnętrznych framework’ow takich jak AutoFac.  W pliku Startup znajdziemy metodę ConfigureServices:

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);

            services.AddMvc();
        }

Jeśli chcemy zarejestrować instancję jako singleton wtedy wystarczy:

services.AddSingleton<ITest, ConcreteTest>();

Nic więcej nie trzeba konfigurować. Instancja automatycznie zostanie wstrzyknięta do kontrolerów jak i widoków. Sprawa w przypadku kontrolerów wygląda prosto i wystarczy po prostu stworzyć konstruktor:

        public HomeController(ITest test)
        {
            _test = test;
        }

W przypadku widoków, również możemy korzystać z wstrzykniętych instancji:

@using Microsoft.Extensions.Configuration
@inject ITest test

@{
    ViewData["Title"] = "About";
}

<h2>@ViewData["Title"].</h2>


<h3>@ViewData["Message"]</h3>




@test.GetText()


AddSingleton tworzy oczywiście pojedynczą instancję. Istnieje również metoda AddTriansient:

services.AddTransient<ITest, ConcreteTest>();

Tworzy ona nową instancję za każdym razem, gdy dochodzi do wstrzyknięcia.

Inny sposób to AddScoped:

services.AddScoped<ITest, ConcreteTest>();

AddScoped będzie współdzielić instancję w ramach tego samego zapytania. Przydatne, kiedy mamy wiele klas do których wstrzykujemy ten sam typ. W wielu przypadkach nie ma potrzeby tworzenia osobnych instancji.

ASP.NET WebAPI – Pakiet Microsoft.Owin.Testing

W poprzednim wpisie pokazałem częściowo pakiet Owin do testowania. Został on użyty do testów dostawcy. Myślę, że zasługuję on na osobny wpis, ponieważ często w testach integracyjnych czy UI jest przydatny.

Owin.Testing to nic innego jak testowy serwer WWW. Zamiast odpalać proces IIS ręcznie w SetUP, można skorzystać z Owin. Stwórzmy zatem projekt dla testów i zainstalujmy kilka pakietów:

1. nUNit
2. Microsoft.Owin.Testing
3. Microsoft.Owin.Host.HttpListener
4. Restsharp

Następnie podstawowy test wygląda następująco:

   [TestFixture]
    public class WebTests
    {
        [Test]
        public void OwinAppTest()
        {
            using (WebApp.Start<Mystartup>("http://localhost:5454"))
            {
                var httpclient = new RestClient("http://localhost:5454");
                var response =  httpclient.Get(new RestRequest("/api/values"));

                string content = response.Content;

                // assert
            }
        }
    }

Widzimy, że musimy stworzyć OWIN startup w aplikacji ASP.NET WebAPI. Tworzymy zatem plik z szablonu:
1

Domyślnie zostanie wygenerowany następująca klasa:

    public class MyStartup
    {
        public void Configuration(IAppBuilder app)
        {
            // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
        }
    }

Musimy umieścić tam kod, który zwykle zostaje wykonany zamiast Global.asax. Oznacza to, że możemy usunąć Global.asax, a następnie umieścić w MyStartup następującą konfigurację:

  public class MyStartup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();

            WebApiConfig.Register(config);

            app.UseWebApi(config);

        }
    }

Aby powyższy kod skompilował się, należy najpierw zainstalować w projekcie WebApi pakiety “Microsoft.AspNet.WebApi.OwinSelfHost” oraz “Microsoft.Owin.Host.SystemWeb”. Pierwszy pakiet jest potrzebny, aby mogło się hostować aplikację w procesie, a nie w IIS. Powyższy test, nie będzie odpalać zatem jakiejkolwiek instancji IIS.

Z kolei drugi pakiet (SystemWeb) jest wymagany w celu odpalenia Startup, w momencie hostowania aplikacji w IIS.

Za pomocą Owin, w łatwy sposób można odpalać aplikacje self-host. Analogiczne pakiety można znaleźć m.in. dla Nancy.

CONSUMER DRIVEN CONTRACTS w PACT-NET – implementacja testów dostawcy

W poprzednim wpisie przedstawiłem framework PACT.NET. Zdołaliśmy zaimplementować testy po stronie konsumentów. Wynikiem tego, był wygenerowany plik JSON zawierający nagrane testy. Dla przypomnienia, test po stronie konsumenta wyglądał następująco:

[TestFixture]
public class ConsumerTests
{
    private IMockProviderService _mockProviderService;
    private IPactBuilder _pactProvider;
 
    [OneTimeSetUp]
    public void OneTimeSetUp()
    {
        _pactProvider = new PactBuilder()
            .ServiceConsumer("ConsumerName")
            .HasPactWith("ProviderName");
 
        _mockProviderService = _pactProvider.MockService(3423,false);
    }
 
    [OneTimeTearDown]
    public void OneTimeTearDown()
    {
        _pactProvider.Build();
    }
 
    [Test]
    public void Should_Return_FirstName()
    {
        //Arrange
        _mockProviderService.Given("there are some persons data")
            .UponReceiving("a request to retrieve all persons")
            .With(new ProviderServiceRequest
            {
                Method = HttpVerb.Get,
                Path = "/api/persons"     
            })
            .WillRespondWith(new ProviderServiceResponse
            {
                Status = 200,
                Headers = new Dictionary<string, string>
                {
                    { "Content-Type", "application/json; charset=utf-8" }
                },
                Body = new[]
                {
                    new
                    {
                        FirstName = "Piotr",
                    }
                }
            });
 
        var consumer = new PersonsApiClient("http://localhost:3423");
 
        //Act
        var persons = consumer.GetAllPersons();
 
        //Assert
        CollectionAssert.IsNotEmpty(persons);
        CollectionAssert.AllItemsAreNotNull(persons.Select(x=>x.FirstName));
 
        _mockProviderService.VerifyInteractions();
    }
}

Z kolei wygenerowany JSON:

{
  "provider": {
    "name": "ProviderName"
  },
  "consumer": {
    "name": "ConsumerName"
  },
  "interactions": [
    {
      "description": "a request to retrieve all persons",
      "provider_state": "there are some persons data",
      "request": {
        "method": "get",
        "path": "/api/persons"
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json; charset=utf-8"
        },
        "body": [
          {
            "FirstName": "Piotr"
          }
        ]
      }
    }
  ],
  "metadata": {
    "pactSpecificationVersion": "1.1.0"
  }
}

Przejdźmy zatem do testu po stronie dostawcy:

    [TestFixture]
    public class ProducerTests
    {
        [Test]
        public void Ensure_That_Consumer_Requirements_Are_Met()
        {
            var config = new PactVerifierConfig();
            IPactVerifier pactVerifier = new PactVerifier(() => { }, () => { }, config);

            pactVerifier
                .ProviderState(
                    "there are some persons data",
                    setUp: DataSetup,tearDown:DataTearDown);
       
            using (var testServer = TestServer.Create<Startup>())
            {
                pactVerifier
                   .ServiceProvider("Persons API", testServer.HttpClient)
                   .HonoursPactWith("Consumer")
                   .PactUri(@"consumername-providername.json")
                   .Verify();
            }
        }

        private void DataTearDown()
        {
        }


        private void DataSetup()
        {
        }

    }

W celu uruchomienia serwera, używamy Microsoft.Owin.Testing czyli klasy TestServer. W momencie odpalenia testu, zostanie zatem stworzony serwer z naszą aplikacją. Następnie “odgrywany” będzie test wygenerowany przez konsumenta. Operujemy na prawdziwych danych, a nie na mock’ach, stąd metody DataSetup oraz DataTearDown w praktyce zawierają odpowiedni kod, dodający oraz usuwający dane testowe.

Po wykonaniu testu, zostanie wygenerowany plik persons_api_verifier.log:

2016-06-01 19:54:01.894 +01:00 [Debug] Verifying a Pact between ConsumerName and ProviderName
  Given there are some persons data
    a request to retrieve all persons
      with GET /api/persons
        returns a response which
          has status code 200
          includes headers
            'Content-Type' with value application/json; charset=utf-8
          has a matching body (FAILED - 1)

Failures:

1) Expected: [
  {
    "FirstName": "Piotr"
  }
], Actual: [
  {
    "FirstName": "Piotr",
    "LastName": "Zielinski"
  },
  {
    "FirstName": "first name",
    "LastName": "last name"
  }
]

Widzimy, że test zakończył się niepowodzeniem, ponieważ oczekiwana zawartość Content jest inna niż ta zwrócona przez prawdziwy serwis.

Za pomocą PathVerifier definiujemy, które testy chcemy wykonać. W naszym przypadku jest tylko jeden. W praktyce definiuje się wiele stanów i kilka plików JSON.

ASP.NET Core RC2 – mapowanie pliku konfiguracyjnego na POCO

Często tworzymy pewien “wrapper” dla wartości pochodzących z Web.Config, np.:

class Config
{
    public int MaxResults{get;set;}
}

Następnie np. w IoC inicjalizujemy obiekt wartościami pochodzącymi z Web.Config:

Config config = new Config();
config.MaxResults = ConfigutationManager.AppSettings["MaxResults].Value;

W ASP.NET Core istnieje łatwy sposób mapowania wartości bezpośrednio na klasę w C#. Oczywiście coś podobnego było i tak możliwe w starych wersjach ASP.NET, ale nie było to tak wygodne.

Załóżmy, że mamy następujący plik konfiguracyjny w ASP.NET Core (plik settings.json):

{
"ApplicationInsights": {
"InstrumentationKey": ""
},
"Config": {
"MaxResults": 15
},

"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

Przede wszystkim musimy zainstalować pakiet “Microsoft.Extensions.Options.ConfigurationExtensions”, np. poprzez dodanie odpowiedniego wpisu w project.json:

"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0-rc2-final"

W Start.ConfigureService dodajemy następujący kod:

            services.AddOptions();
            services.Configure<Config>(Configuration.GetSection("Config"));

Pierwsza linia umożliwi m.in. wstrzyknięcie Config do kontrolera. Druga z kolei, naturalnie czyta wartości z pliku JSON. Samo wstrzyknięcie sprowadza się do zdefiniowania odpowiedniego konstruktora:

        private readonly IOptions<Config> _configValue;

        public HomeController(IOptions<Config> _configValue )
        {
            this._configValue = _configValue;
        }

         public IActionResult About()
        {
            ViewData["Message"] = _configValue.Value.MaxResults.ToString();

            return View();
        }

Dosyć zwięzłe rozwiązanie. Nie trzeba samemu wstrzykiwać do kontenera IoC czy nawet odczytywać konkretnych wartości z pliku konfiguracyjnego.

.NET Core oraz ASP.NET Core

.NET Core to nowy framework od Microsoft’u, który aktualnie wciąż jest w produkcji. Zdecydowałem się napisać dzisiejszy post, ponieważ w ostatnim czasie nastąpiło wiele zmian w wersjonowaniu i nazewnictwie .NET. Moim zdaniem wprowadziło to trochę zamieszania.

Przede wszystkim, co to jest .NET Core? To nowa wersja framework’a, która z założenia ma być wieloplatformowa oraz open-source. To wielka zmiana ze strony Microsoft, ale już od kilku lat można obserwować, że Microsoft zmierzą w kierunku, który w latach 90 był kompletnie nie do pomyślenia. .NET Core będzie zatem działać zarówno na Windows jak i Mac czy Linux.

Druga różnica to fakt, że .NET Core to po prostu pakiet NuGet. Doskonale nadaje się to do aplikacji webowych. Dzisiaj, serwer web musi mieć zainstalowany .NET Framework. Oznacza to, że ciężko mieć kilka aplikacji ASP.NET uruchomianych w różnych wersjach .NET Framework.  Dzisiejsza wersja .NET Framework jest bardzo mocno powiązana z systemem operacyjnym i z punktu architektonicznego stanowi monolit.

Wchodząc trochę w szczegóły techniczne, .NET Core składa się z podstawowej biblioteki klas (CoreFx), oraz środowiska uruchomieniowego CoreClr. Na GitHub można śledzić postęp prac:

https://github.com/dotnet/corefx

https://github.com/dotnet/coreCLR

Ktoś może zapytać się, ale co z Mono? Aktualnie jest to implementacja .NET na Linuxa. Po wprowadzeniu .NET Core nie jest jeszcze jasne co stanie się z Mono, ponieważ jest to niezależny framework. Na dzień dzisiejszy, wydaje mi się, że w pewnym czasie .NET Core zastąpi całkowicie Mono.

Wprowadzenie .NET Core nie oznacza, że nagle będziemy mogli uruchamiać wszystkie aplikacje na Linux. .NET Core to jedynie podzbiór .NET Framework. Wiadomo, że główną motywacją były aplikacje webowe czyli ASP.NET. Inne typy aplikacji typu WPF nie będą oczywiście dostępne (przynajmniej bazując na informacjach, które są już dostępne).

Nie trudno teraz domyślić się, że ASP.NET Core to nowa wersja ASP.NET,  napisana pod .NET Core. Oznacza to, że będzie mogłaby być hostowana na Linux. Analogicznie do .NET Core, ASP.NET Core jest również open-source.

ASP.NET 5, o którym pisałem wiele razy na blogu, został zmieniony na ASP.NET Core. Informacje, które wcześniej podawałem,  dotyczą zatem ASP.NET Core, a nie ASP.NET 5, którego nazwa została zmieniona.  Jedną z większych zmian ASP.NET Core to wprowadzenie projects.json, zamiast packages.config. Więcej szczegółów można znaleźć w poprzednich wpisach.

Podsumowując, .NET Core oraz ASP.NET Core dadzą nam:

  • Wieloplatformowość
  • Modularność i niezależność od .NET Framework zainstalowanego na systemie – każda aplikacja, będzie mogą korzystać z innej wersji framework’a.
  • Open-source

Warto również przejrzyj się następującemu diagramowi (źródło  http://www.hanselman.com):

Jak widzimy, ASP.NET Core 1.0, będzie działać również na starym, monolitowym frameworku (.NET Framework 4.6). Taka konfiguracja też jest możliwa tzn. ASP.NET Core  + .NET Framework, ale w celu uzyskania wieloplatformowości należy zainteresować się ASP.NET Core 1.0 + .NET Core.

HTTP 2.0 Server Push

Dzisiaj kolejny element HTTP 2.0, tym razem wymagający zmiany kodu po stronie aplikacji. Tak jak już z wszystkimi opisanymi wcześniej zmianami, ma to na celu zmniejszenie opóźnienia (latency) wynikającego z liczby zapytań.

Doskonale wiemy, że każda strona ma referencje do innych zasobów takich jak CSS czy pliki graficzne. Wcześniej zajęliśmy się już HTTP Multiplexing, który znacząco niweluje problem.

W jednym z poprzednich wpisów pokazałem również jak w HTTP 1.1 programiści radzili sobie z zasobami. Częstym obejściem, było umieszczenie plików graficznych inline. Powodowało to, że jak tylko plik HTML lub CSS został ściągnięty, plik graficzny stawał się od razu dostępny (ponieważ był częścią tego samego pliku).

Z tej techniki tak naprawdę wywodzi się Server Push. Wiemy, że w celu wyświetlenia strony “A”, musimy również wysłać zasób “B”. Dlaczego zatem nie wysłać zasobu B od razu zaraz po A? Inline jest pewną implementacją tego, ale jak wiemy dość kłopotliwą. Server push pozwala z kolei, “wepchnąć” dowolny zasób (nie koniecznie plik graficzny) w strumień danych płynący od serwera do klienta. Oczywiście serwer (np. IIS), nie będzie znał relacji w poszczególnych aplikacjach między zasobami. Wynika z tego, że to nasza rola (ewentualnie framework’a)  jest poinformować o tym klienta.

Służą do tego tzw. “Push Promise”, czyli obietnice klienta odnoście relacji między zasobami. Jeśli projektując system wiemy, że wyświetlając stronę A, zaraz będzie załadowana  biblioteka jQuery,  wtedy wydajemy obietnice. Push promise to prosta metoda, która zwraca listę zależności. Przeglądarka następnie przeczyta taką listę obietnic i stwierdzi czy warto je akceptować. Czasami te zasoby mogą już znajdować się w cache przeglądarki, wtedy należy przerwać taką transmisje. W przeciwnym wypadku przeglądarka zaakceptuje je i w praktyce zostaną one odebrane zaraz po przesłaniu strony – bez zbędnych opóźnień.

W ASP.NET dostępna jest już metoda PushPromise:

public void PushPromise(
	string path,
	string method,
	NameValueCollection headers
)

Drugie przeładowanie jest nieco prostsze w użyciu:

public void PushPromise(
	string path
)

Warto zauważyć, że w taki sposób możemy kontrolować czas życia obiektu. W przypadku inline nie było takiej możliwości – zawsze zasób był przesyłany. Jeśli korzystamy z ServerPush, zasób będący w cache przeglądarki zostanie anulowany i nie przesyłany. Podobnie za pomocą parametru headers (nagłówki), możemy dowolnie aktualizować lub usuwać dany zasób z cache (nagłówek Cache-Control).

Z powyższego opisu wynika jeszcze jeden wniosek – zwracana obietnica zasobu musi nadawać się do cachowania. Przeglądarka ma prawo i będzie wspomniany zasób wykorzystywać w innych podstronach. Tak samo, projektując stronę, nie powinniśmy polegać wyłącznie na Server Push. To jedynie usprawnienie w wydajności, a nie element nowej architektury – wiele przeglądarek wciąż tego nie wspiera.

Server Push to wyłącznie mechanizm uprzedzenia przeglądarki i dostarczania zależności wraz z plikiem głównym. Nie ma to nic wspólnego z Web Sockets i innymi technikami komunikacja dwustronnej.

HTTP 2.0 w IIS (ASP.NET)

IIS w Windows 10 wspiera już od jakiegoś czasu nową wersję protokołu.  W zasadzie prawdopodobnie nic nie musimy robić, jeśli posiadamy prawidłową wersję IIS. Oczywiście użytkownicy muszą posiadać również odpowiednią wersję przeglądarki internetowej. Ich kompatybilność można sprawdzić tutaj. Jak widać, Edge, Chrome, Firefox, Opera czy iOS Safari radzą sobie najlepiej. W przypadku IE, najnowsza wersja wspiera HTTP 2.0 tylko częściowo.

Jeśli jeszcze nie zainstalowaliśmy IIS na Windows 10, wtedy przejdźmy najpierw do “Turn Windows features on or off”, a następnie zaznaczamy Internet Information Service:

1

Aktualnie IIS wspiera wyłącznie HTTPS (TLS). Oznacza to, że jeśli chcemy użyć nieszyfrowanego połączenia to HTTP 1.1 będzie wciąż używany. Dodajmy więc kolejny binding HTTPS:

2

I to wszystko co musimy zrobić… Jak widać,  nic specjalnego nie należy konfigurować. Wystarczy odpowiednia wersja IIS oraz przeglądarki po stronie klienta. Odpalmy zatem stronę zarówno z HTTP, jak i HTTPS. Spodziewamy się, że nieszyfrowane połączenie wciąż będzie HTTP 1.1:

3

W przypadku HTTPS,HTTP 2.0 będzie użyty: 4

Skrót H2 oznacza oczywiście HTTP 2.0

W przyszłym poście opiszę kolejny element HTTP 2.0, w tym przypadku Server Push, który wymaga pewnych modyfikacji w aplikacji ASP.NET. W celu przetestowania tej aplikacji, potrzebny będzie zarówno serwer IIS wspierający HTTP 2.0, jak i odpowiednia przeglądarka (np. Chrome).