Back to "Hinzufügen eines Clients für Umami API"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Umami

Hinzufügen eines Clients für Umami API

Wednesday, 14 August 2024

Einleitung

In diesem Beitrag werde ich Ihnen zeigen, wie man einen Client für die Umami-Reporting-API erstellt.Das ist ein einfaches Beispiel, das zeigt, wie Sie sich mit der API authentifizieren und Daten daraus abrufen können.

Hier finden Sie alle Quellcodes auf meinem GitHub Repo.

Voraussetzungen

Installieren Sie Umami-.. Hier finden Sie die Installationsanleitung Hierher diese Details, wie ich Umami installieren und verwenden, um Analysen für diese Website zur Verfügung stellen

Auch dies ist eine einfache Implementierung von ein paar der Umami Website Stats API Endpunkte. Sie finden die vollständige API-Dokumentation Hierher.

In diesem habe ich mich entschieden, die folgenden Endpunkte zu implementieren:

  • GET /api/websites/:websiteId/pageviews Wie der Name schon andeutet, gibt dieser Endpunkt die Seitenansichten und die Sitzungsdateien für eine bestimmte Website über einen bestimmten Zeitraum zurück.
{
  "pageviews": [
    { "x": "2020-04-20 01:00:00", "y": 3 },
    { "x": "2020-04-20 02:00:00", "y": 7 }
  ],
  "sessions": [
    { "x": "2020-04-20 01:00:00", "y": 2 },
    { "x": "2020-04-20 02:00:00", "y": 4 }
  ]
}
  • GET /api/websites/:websiteId/stats Dies liefert grundlegende Statistiken für eine bestimmte Website.
{
  "pageviews": { "value": 5, "change": 5 },
  "visitors": { "value": 1, "change": 1 },
  "visits": { "value": 3, "change": 2 },
  "bounces": { "value": 0, "change": 0 },
  "totaltime": { "value": 4, "change": 4 }
}
  • GET /api/websites/:websiteId/metrics Dies gibt die Metriken für eine bestimmte Website bu URL usw. zurück.
[
  { "x": "/", "y": 46 },
  { "x": "/docs", "y": 17 },
  { "x": "/download", "y": 14 }
]

Wie Sie aus den docs sehen können, akzeptieren diese alle eine Reihe von Parametern und ich habe diese als Abfrageparameter im Code unten dargestellt.

Testen im Rider httpClient

Ich beginne immer mit dem Testen der API in Rider-'s build-MSC1in HTTP-Client-.. Dies erlaubt es mir, die API schnell zu testen und die Antwort zu sehen.

### Login Request and Store Token
POST https://{{umamiurl}}/api/auth/login
Content-Type: application/json

{
  "username": "{{username}}",

  "password": "{{password}}"
}
> {% client.global.set("auth_token", response.body.token);
    client.global.set("endAt", Math.round(new Date().getTime()).toString() );
    client.global.set("startAt", Math.round(new Date().getTime() - 7 * 24 * 60 * 60 * 1000).toString());
%}


### Use Token in Subsequent Request
GET https://{{umamiurl}}/api/websites/{{websiteid}}/stats?endAt={{endAt}}&startAt={{startAt}}
Authorization: Bearer {{auth_token}}

### Use Token in Subsequent Request
GET https://{{umamiurl}}/api/websites/{{websiteid}}/pageviews?endAt={{endAt}}&startAt={{startAt}}&unit=day
Authorization: Bearer {{auth_token}}


###
GET https://{{umamiurl}}}}/api/websites/{{websiteid}}/metrics?endAt={{endAt}}&startAt={{startAt}}&type=url
Authorization: Bearer {{auth_token}}

Es ist eine gute Praxis, um die variablen Namen hier zu halten {{}} eine env-.-json-Datei, auf die Sie sich beziehen können, wie unten.

{
  "local": {
    "umamiurl":"umamilocal.mostlylucid.net",
    "username": "admin",
    "password": "<password{>",
    "websiteid" : "32c2aa31-b1ac-44c0-b8f3-ff1f50403bee"
  }
}

Einrichtung

Zuerst müssen wir HttpClient und die Dienste konfigurieren, die wir verwenden werden, um die Anfragen zu machen


public static class UmamiSetup
{
    public static void SetupUmamiServices(this IServiceCollection services, IConfiguration config)
    {
        var umamiSettings = services.ConfigurePOCO<UmamiSettings>(config.GetSection(UmamiSettings.Section));
        services.AddHttpClient<AuthService>(options =>
        {
            options.BaseAddress = new Uri(umamiSettings.BaseUrl);
            
        }) .SetHandlerLifetime(TimeSpan.FromMinutes(5))  //Set lifetime to five minutes
        .AddPolicyHandler(GetRetryPolicy());;
        services.AddScoped<UmamiService>();
        services.AddScoped<AuthService>();

    }
    static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg =>  msg.StatusCode == HttpStatusCode.ServiceUnavailable)
            .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }

}

Hier konfigurieren wir die Einstellungsklasse UmamiSettings und fügen Sie die AuthService und UmamiService zu der Dienstleistungensammlung-.- Wir fügen auch eine retry Richtlinie zum HttpClient hinzu, um vorübergehende Fehler zu handhaben-MSC1-

Als nächstes müssen wir die UmamiService und AuthService Klassen ,.-

Das AuthService ist einfach verantwortlich für das Erhalten des JWT-Tokens aus der API.

public class AuthService(HttpClient httpClient, UmamiSettings umamiSettings, ILogger<AuthService> logger)
{
    private string _token;
    public HttpClient HttpClient => httpClient;

    public async Task<bool> LoginAsync()
    {
        var loginData = new
        {
            username = umamiSettings.Username,
            password = umamiSettings.Password
        };

        var content = new StringContent(JsonSerializer.Serialize(loginData), Encoding.UTF8, "application/json");
        var response = await httpClient.PostAsync("/api/auth/login", content);

        if (response.IsSuccessStatusCode)
        {
            var authResponse = await response.Content.ReadFromJsonAsync<AuthResponse>();


            _token = authResponse.Token;
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);
            logger.LogInformation("Login successful");
            return true;
        }

        logger.LogError("Login failed");
        return false;
    }
}

Hier haben wir eine einfache Methode LoginAsync die eine POST-Anfrage an die /api/auth/login Endpoint mit Benutzername und Passwort.....Wenn die Anfrage erfolgreich ist, speichern wir das JWT-Token im _token Feld und setzen Sie die Authorization Kopfzeile auf dem Http-Client

Das UmamiService ist verantwortlich für die Anfragen an die API.. Für jede der Hauptmethoden I'-ve definierte Request-Objekte, die alle Parameter für jeden Endpunkt akzeptieren..- Dies macht es einfacher, den Code zu testen und aufrechtzuerhalten.

Sie alle folgen einem ähnlichen Muster, also zeigt I''ll einfach einen von ihnen hier.

public async Task<UmamiResult<StatsResponseModels>> GetStatsAsync(StatsRequest statsRequest)
{
    // Start building the query string
    var queryParams = new List<string>
    {
        $"start={statsRequest.StartAt}",
        $"end={statsRequest.EndAt}"
    };

    // Add optional parameters if they are not null
    if (!string.IsNullOrEmpty(statsRequest.Url)) queryParams.Add($"url={statsRequest.Url}");
    if (!string.IsNullOrEmpty(statsRequest.Referrer)) queryParams.Add($"referrer={statsRequest.Referrer}");
    if (!string.IsNullOrEmpty(statsRequest.Title)) queryParams.Add($"title={statsRequest.Title}");
    if (!string.IsNullOrEmpty(statsRequest.Query)) queryParams.Add($"query={statsRequest.Query}");
    if (!string.IsNullOrEmpty(statsRequest.Event)) queryParams.Add($"event={statsRequest.Event}");
    if (!string.IsNullOrEmpty(statsRequest.Host)) queryParams.Add($"host={statsRequest.Host}");
    if (!string.IsNullOrEmpty(statsRequest.Os)) queryParams.Add($"os={statsRequest.Os}");
    if (!string.IsNullOrEmpty(statsRequest.Browser)) queryParams.Add($"browser={statsRequest.Browser}");
    if (!string.IsNullOrEmpty(statsRequest.Device)) queryParams.Add($"device={statsRequest.Device}");
    if (!string.IsNullOrEmpty(statsRequest.Country)) queryParams.Add($"country={statsRequest.Country}");
    if (!string.IsNullOrEmpty(statsRequest.Region)) queryParams.Add($"region={statsRequest.Region}");
    if (!string.IsNullOrEmpty(statsRequest.City)) queryParams.Add($"city={statsRequest.City}");

    // Combine the query parameters into a query string
    var queryString = string.Join("&", queryParams);

    // Make the HTTP request
    var response = await authService.HttpClient.GetAsync($"/api/websites/{WebsiteId}/stats?{queryString}");

    if (response.IsSuccessStatusCode)
    {
        var content = await response.Content.ReadFromJsonAsync<StatsResponseModels>();
        return new UmamiResult<StatsResponseModels>(response.StatusCode, response.ReasonPhrase ?? "Success", content ?? new StatsResponseModels());
    }

    if (response.StatusCode == HttpStatusCode.Unauthorized)
    {
        await authService.LoginAsync();
        return await GetStatsAsync(statsRequest);
    }

    logger.LogError("Failed to get stats");
    return new UmamiResult<StatsResponseModels>(response.StatusCode, response.ReasonPhrase ?? "Failed to get stats", null);
}

Hier können Sie sehen, dass ich das Anfrageobjekt nehme

public class BaseRequest
{
    public long StartAt => StartAtDate.ToMilliseconds(); // Timestamp (in ms) of starting date
    public long EndAt => EndAtDate.ToMilliseconds(); // Timestamp (in ms) of end date
    public DateTime StartAtDate { get; set; }
    public DateTime EndAtDate { get; set; }
}
public class StatsRequest : BaseRequest
{
    // Optional properties
    public string? Url { get; set; } // Name of URL
    public string? Referrer { get; set; } // Name of referrer
    public string? Title { get; set; } // Name of page title
    public string? Query { get; set; } // Name of query
    public string? Event { get; set; } // Name of event
    public string? Host { get; set; } // Name of hostname
    public string? Os { get; set; } // Name of operating system
    public string? Browser { get; set; } // Name of browser
    public string? Device { get; set; } // Name of device (e.g., Mobile)
    public string? Country { get; set; } // Name of country
    public string? Region { get; set; } // Name of region/state/province
    public string? City { get; set; } // Name of city
}

Und bauen Sie den Abfragestring aus den Parametern....Wenn die Anfrage erfolgreich ist, geben wir den Inhalt als UmamiResult Wenn die Anfrage mit einem Status-Code von 401' fehlschlägt, rufen wir die LoginAsync Methode und wiederholen Sie die Anfrage Dies stellt sicher, dass wir elegant, '... die Token-Auslauf behandeln.

Schlußfolgerung

Dies ist ein einfaches Beispiel dafür, wie man einen Client für die Umami API erstellt.Sie können dies als Ausgangspunkt nutzen, um komplexere Clients zu erstellen oder die API in Ihre eigenen Anwendungen zu integrieren.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.