Back to "C#-clientin lisääminen Umami API:hun"

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

C#-clientin lisääminen Umami API:hun

Wednesday, 14 August 2024

Introduction

Tässä artikkelissa osoitan, miten luoda C-clienti Umami raportointi API:lle. Tämä on yksinkertainen esimerkki, jossa esitetään, kuinka todennetaan API:n avulla ja saadaan data siitä

Voitte löytää tämän koko lähdekoodin GitHubin repo:ssa.

Edellytykset

Asetus Umami. Asetusohjeet ovat saatavilla täällä tämä selittää, miten otan käyttöön Umamin siteen analysointiin..

Jälleen kerran, tämä on yksinkertainen täytäntöönpano muutamasta Umami Website Stats API:n loppukohdasta . saataisiin kaikki API-asiakirjat täällä.

Tässä olen päättänyt toteuttaa seuraavat loppukohdat:

  • GET /api/websites/:websiteId/pageviews - Kuten nimi viittaa, ,\ tämä loppupiste palauttaa tietyn verkkosivuston sivukatsauksen ja \ '\ istuntojaksot\ M SK3\ tietyn ajanjakson aikana
{
  "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 - se palauttaa tietyn verkkosivuston perustilastotM SK1
{
  "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 - tämä palauttaa tietyn verkkosivuston URL:n mittarit jne.
[
  { "x": "/", "y": 46 },
  { "x": "/docs", "y": 17 },
  { "x": "/download", "y": 14 }
]

Kuten näette asiakirjoista, nämä kaikki hyväksyvät joukon parametreja ( ja olen esittänyt ne hakuparametereiksi jäljempänä olevassa koodissa

Testi Rider httpClientissä

Aloitan aina testaamalla API:ta Riderissä.

### 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}}

”'” on hyvä käytäntö säilyttää muuttuvat nimet tässä {{}} env.json-tietokanta, johon voi viitata seuraavasti

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

Järjestelmä

Ensinnäkin meidän on konfiguroitava HttpClient ja palvelut, joita käytämme pyyntöjen tekemiseen.


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)));
    }

}

Tässä konfiguroimme sääntöjen luokkaa UmamiSettings ja lisätään AuthService ja UmamiService palvelujen keräämiseen. Lisämme myös uudelleentryynnin politiikkaa HttpClientille väliaikaisten virheiden käsittelemiseksi

Seuraavaksi meidän on luotava UmamiService ja AuthService luokkat.

Euroopan unionin AuthService on yksinkertaisesti vastuussa JWT-token saamista API:sta

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;
    }
}

Tässä meillä on yksinkertainen menetelmä LoginAsync joka lähettää POST-pyynnön /api/auth/login lopullinen kohta, johon sisältyy käyttäjänimi ja -tunnus. Jos pyyntö on onnistunutM SK1 talletamme JWT-tokenin _token ja asettaa Authorization header HttpClientissä.

Euroopan unionin UmamiService on vastuussa API:n pyyntöjen esittämisestä Kaikissa päämenetelmissä I' on määritelty pyynnöt, jotka hyväksyvät kaikki parametrit kullekin loppukohdalle.

Ne kaikki noudattavat simiiar-mallia, joten esitän niistä yhden täällä.

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);
}

Tässä voitte nähdä, että otan pyynnön kohteen

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
}

Ja rakennamme kysymyksen stringia parametreista. Jos pyyntö on onnistunut, palautamme sen sisällön UmamiResult object. Jos pyyntö epäonnistuu, kutsumme LoginAsync menetelmä ja toistaa pyynnön. Tämä varmistaa, että käsittelemme M SK1elegaattisesti ' token voimassaolon päättymistä

Päätelmät

Tämä on yksinkertainen esimerkki siitä, miten luoda C-clienti Umami API:lle#. Voitte käyttää sitä lähtökohtana monimutkaisempien asiakkaiden rakentamiseksi tai API:n integroimiseksi omiin sovelluksiinne.

logo

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