Back to "Προσθήκη ενός C# Client για το 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

Προσθήκη ενός C# Client για το Umami API

Wednesday, 14 August 2024

εισαγωγή

Σε αυτό το post, θα σας δείξω πώς να δημιουργήσετε ένα C# client για το Umami reporting APIM SK2 Αυτό είναι ένα απλό παράδειγμα που δείχνει πώς να αυθεντικοποιηθεί με την API και να ανακτήσει δεδομένα από αυτήνΜSK3

Μπορείτε να βρείτε όλο το κώδικα πηγής για αυτό Το GitHub repo.

Προϋποθέσεις

Instal Umami. Μπορείτε να βρείτε τις οδηγίες εγκατάστασης ΕΔΩ Αυτές οι λεπτομέρειες για το πώς μπορώ να εγκαταστήσω και να χρησιμοποιήσω το Umami για την παροχή αναλύσεων για αυτόν τον ιστότοπο.

Ξανά,Αυτό είναι μια απλή εφαρμογή μερικών από τα καταστήματα της ιστοσελίδας του Umami Stats API endpoints.Μπορείτε να βρείτε την πλήρη τεκμηρίωση API ΕΔΩ.

Σε αυτό έχω επιλέξει να εφαρμόσω τα ακόλουθα τελικά σημεία:

  • GET /api/websites/:websiteId/pageviews - Όπως υποδηλώνει το όνομαΜSK1 αυτό το τελικό σημείο επιστρέφει τις προβολές σελίδων και Σημεία αναφοράς της 'Η ' για έναν συγκεκριμένο ιστότοπο κατά τη διάρκεια μιας χρονικής περιόδου Η .
{
  "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 - αυτό επιστρέφει τις βασικές στατιστικές για μια δεδομένη ιστοσελίδαΜ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 - αυτό επιστρέφει τις μετρήσεις για μια δεδομένη ιστοσελίδα bu URL κλπΜSK1
[
  { "x": "/", "y": 46 },
  { "x": "/docs", "y": 17 },
  { "x": "/download", "y": 14 }
]

Όπως μπορείτε να δείτε από τα docs, όλα αυτά αποδέχονται μια σειρά παραμέτρων ΜSK1 και έχω αντιπροσωπεύσει αυτές ως παράμετροι ερωτήματος στον κώδικα κάτω).

Δοκιμή στο Rider HTTPClient

Πάντα ξεκινάω δοκιμάζοντας το API στο Rider' κατασκευάστηκε- διατηρεί τον πελάτη HTTPM SK2 Αυτό μου επιτρέπει να ελέγξω γρήγορα το API και να δω την απάντησηΜSK3

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

Το ' διατηρεί καλή πρακτική για να κρατήσει τα μεταβλητά ονόματα εδώ {{}} ένα env.json αρχείο το οποίο μπορείτε να αναφέρετε όπως παρακάτω.

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

Ρυθμίσεις

Πρώτα πρέπει να διαμορφώσουμε την HttpClient και τις υπηρεσίες που θα χρησιμοποιήσουμε για να κάνουμε τις αιτήσεις.


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

}

Εδώ ρυθμίζουμε την κατηγορία ρυθμίσεων UmamiSettings Και προσθέστε το AuthService και UmamiService στην συλλογή υπηρεσιών. Προσθέτουμε επίσης μια πολιτική αποχώρησης στο HttpClient για την αντιμετώπιση μεταβατικών σφαλμάτων.

Στη συνέχεια θα πρέπει να δημιουργήσουμε το UmamiService και AuthService ΕΠΙΧΕΙΡΕΥΣΗ ΚΟΙΝΩΝΙΑΣ

Η AuthService είναι απλώς υπεύθυνος για την απόκτηση του token JWT από το 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;
    }
}

Εδώ έχουμε μια απλή μέθοδο LoginAsync που αποστέλλει ένα αίτημα στο /api/auth/login τελικό σημείο με το όνομα χρήστη και τον κωδικό πρόσβασης. Εάν η αίτηση είναι επιτυχής, αποθηκεύουμε το token JWT στο _token Το πεδίο και η Authorization Ο τίτλος του HttpClient.

Η UmamiService είναι υπεύθυνος για την υποβολή αιτήσεων στο API. Για κάθε μία από τις κύριες μεθόδους που προτιμώ το ' διατηρεί καθορισμένα αντικείμενα αιτήσεων τα οποία αποδέχονται όλες τις παραμέτρους για κάθε τελικό σημείο του . Αυτό διευκολύνει την δοκιμή και τη συντήρηση του κώδικα των .

Όλοι ακολουθούν ένα μοντέλο simiiar,, οπότε εγώΜSK1 θα δείξω μόνο ένα από αυτά εδώ.

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

Εδώ μπορείτε να δείτε ότι παίρνω το αντικείμενο της αίτησης

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
}

Και χτίστε τη γραμμή ερωτήσεων από τις παραμέτρους. Αν η αίτηση είναι επιτυχής, επιστρέφουμε το περιεχόμενο ως ένα UmamiResult αντικείμενο. Εάν η αίτηση αποτυγχάνει με ΜSK1 κώδικα κατάστασης ΜSK2 καλούμε το LoginAsync μέθοδος και ανάκληση του αιτήματος.Αυτό εξασφαλίζει ότι ΜSK1εξαιρετικά' χειρίζεται την ημερομηνία λήξης του tokenM SK3

Συμπεράσματα

Αυτό είναι ένα απλό παράδειγμα του πώς να δημιουργήσετε έναν C# client για το Umami API. Μπορείτε να χρησιμοποιήσετε αυτό ως σημείο εκκίνησης για την κατασκευή πιο σύνθετων πελατών ή ενσωμάτωση της API στις δικές σας εφαρμογέςM SK2

logo

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