16.09.20213 min

Szymon Rożek.NET Developer

Co to jest ETag i jak go zaimplementować w .NET

Poznaj ETag, czyli header protokołu HTTP i dowiedz się, jak zaimplementować go w .NET.

Co to jest ETag i jak go zaimplementować w .NET

W dzisiejszych czasach prawie każda aplikacja czy też strona internetowa jest wypełniona mnóstwem danych. Większość z nich nieustannie się zmienia oraz jest aktualizowana po stronie użytkownika. Każde odświeżenie spowalnia aplikację, dlatego dane powinny być przeładowywane tylko wtedy, gdy różnią się od swojej poprzedniej wersji. 

Z pomocą przychodzi header protokołu HTTP, a mianowicie – ETag. Odpowiada on za identyfikowanie wersji zasobów zwracanych przez serwer. Wartość ETag może być tworzona na wiele sposobów. Używane do tego są przeważnie algorytmy hashujące, aczkolwiek nie jest to wymagane.


Najważniejsze jest to, aby mieć pewność, iż wygenerowana wartość będzie unikalna. Jeśli użyjemy zbyt słabego generatora, możemy uzyskać dla różnych wersji zasobów ten sam identyfikator, co doprowadzi do niepoprawnego działania. 

Zawsze gdy dane dostępne pod konkretnym endpointem ulegną zmianie, powinna zostać wygenerowana nowa wartość Etag. Aby dodać go do odpowiedzi, wystarczy nowy header „ETag” wraz z wygenerowaną przez nas wartością identyfikującą konkretne zasoby: ETag: "etag value".

Po wykonaniu zapytania oraz uzyskaniu w odpowiedzi wartości ETag powinniśmy ją zapisać po stronie użytkownika. Podczas przygotowywania kolejnego requestu uzyskaną wartość musimy dodać jako header „If-None-Match”. 

W przypadku gdy wysłana wartość jest taka sama jak wartość ETag zwracana w danym momencie przez serwer, zamiast kodu odpowiedzi 200 zwrócony zostanie 304, czyli „Not modified”.

Poniżej przykład zastosowania takiego rozwiązania w .NET.:

Stworzenie nowego atrybutu:

internal sealed class ETagFilter : Attribute, IActionFilter
{
    private readonly int[] _statusCodes;

    public ETagFilter(params int[] statusCodes)
    {
        _statusCodes = statusCodes;
        if (statusCodes.Length == 0) _statusCodes = new[] { 200 };
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.HttpContext.Request.Method != WebRequestMethods.Http.Get) return;
        if (!((IList) _statusCodes).Contains(context.HttpContext.Response.StatusCode)) return;
        var content = JsonConvert.SerializeObject(context.Result);

        var etag = ETagGenerator.GetETag(Encoding.UTF8.GetBytes(content));
                
        if (context.HttpContext.Request.Headers.Keys.Contains(HeaderNames.IfNoneMatch) && context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch].ToString() == $"\"{etag}\"")
        {
            context.Result = new StatusCodeResult((int) HttpStatusCode.NotModified);
        }
        context.HttpContext.Response.Headers.Add(HeaderNames.ETag, new[] { $"\"{etag}\"" });
    }        
}

internal static class ETagGenerator
{
    public static string GetETag(byte[] contentBytes)
    {
        return GenerateETag(contentBytes);
    }

    private static string GenerateETag(byte[] data)
    {
        using var md5 = MD5.Create();
        var hash = md5.ComputeHash(data);
        var hex = BitConverter.ToString(hash);
        return hex.Replace("-", "");
    }
}


Powyżej został załączony kod atrybutu, który możemy stworzyć w celu implementacji obsługi ETag. 

ETagGenerator jest klasą pomocniczą, która tworzy unikalny string odpowiadający tablicy bajtów, wygenerowanej na podstawie danych, które są zwracane przy zapytaniu. W powyższym rozwiązaniu został użyty algorytm MD5.

W metodzie OnAcionExecuted, która jest wykonywana już po przeprowadzeniu operacji wygenerowania danych po stronie serwera, sprawdzane jest, czy do zapytania został dodany header „If-None-Match”. Jego wartość jest porównywana do wartości generowanej na podstawie danych zwróconych w danym momencie.

Użycie atrybutu:

Aby zastosować filtrację, wystarczy dodać stworzony przez nas atrybut nad konkretnym endpointem.

Przykład działania:

Poniżej do zobrazowania requestów używałem wtyczki do przeglądarki google Talend API Tester – Free Edition.

Pierwsze zapytanie do serwera:


Odpowiedź:


Jak widać w odpowiedzi. zwracana jest lista sklepów wraz z ETagiem.

W drugim zapytaniu otrzymaną wartość ETag musimy dodać jako header „If-None-Match”.

Drugie zapytanie: 


Odpowiedź:


Jak widać nasza logika zadziałała, pobierane dane się nie zmieniły, dlatego wartość przez nas wysłana była równa tej, którą dostaliśmy podczas wykonywania pierwszego podsumowania.

Użycie powyższego rozwiązania pozwala na lepszą wydajność aplikacji klienckich, ponieważ zawsze możemy zweryfikować to, iż pobierane dane niczym nie różnią się od danych pobranych wcześniej. 

<p>Loading...</p>

Dziel się wiedzą ze 160 tysiącami naszych czytelników

Zostań autorem Readme

Hitachi Energy

Security Architect

senior

15 000 - 21 000 PLN

Umowa o pracę

Krakow

Praca zdalna 100%

Ważna do 26.02.2022

Bardzo dobrze
Microsoft Azure and/or AWS

Hitachi Energy

Product Development Manager

senior

15 000 - 20 000 PLN

Umowa o pracę

Krakow

Praca zdalna 100%

Ważna do 26.02.2022

Bardzo dobrze
AgileSoftware Development Life Cycle Leadership skills

Simple SA

Java Developer (Mid/Senior)

medium

7 000 - 15 000 PLN

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 26.02.2022

Dobrze
JavaSpringSpring Boot

Asseco Poland S.A.

Administrator / Starszy Administrator Systemów IT

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 26.02.2022

Dobrze
PostgreSQLBash

Nokia

5G Automation Engineer, IODT

medium

Brak widełek

Umowa o pracę

Wrocław

Praca zdalna 100%

Ważna do 13.03.2022

Divante

Senior Vue.js Developer

senior

15 300 - 23 500 PLN

Kontrakt B2BUmowa o pracę

Wrocław

Praca zdalna 100%

Ważna do 13.03.2022

Dobrze
JavaScriptTypeScriptVue.js

T-Mobile Polska S. A.

Frontend Developer

medium

Brak widełek

Kontrakt B2B

Warsaw

Ważna do 26.02.2022

Bardzo dobrze
ReactReduxNode.js

Commerzbank - Centrum Technologii Cyfrowych w Polsce

Business Expert for Risk Applications

medium

Znamy widełki

Umowa o pracę

Łódź

Ważna do 26.02.2022

Dobrze
SQLMS Office
Początkująco
SAS / R / Python

Commerzbank - Centrum Technologii Cyfrowych w Polsce

Business Expert with German for Risk Analytics

medium

Znamy widełki

Umowa o pracę

Łódź

Ważna do 26.02.2022

Dobrze
MS Office
Początkująco
SQL / VBA / PythonQlik Sense / Qlik View / Arcadia