Προσθήκη ενός C# Client για το Umami API
εισαγωγή
Σε αυτό το 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