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
Een C#-Client toepassen voor Umami API
Wednesday, 14 August 2024
Inleiding
In deze post zal ik jullie laten zien hoe je een C-client maakt voor de Umami-rapportatie-API. Dit is een eenvoudig voorbeeld dat demonstreert hoe je met de API authentifieert en data eruit haalt.
Je kunt hier alle broncodes voor vinden. op mijn GitHub repo..
Voorwaarden
Installeer Umami. Je kunt de installering instructies vinden. Hier. Dit vertelt hoe ik Umami installeer en gebruik om analyses voor deze site te leveren.
Nogmaals, dit is een eenvoudige implementatie van een paar van de Umami Website Stats API-endpoints . Je kunt de volledige API-documentatie vinden. Hier..
Hier heb ik gekozen om de volgende eindpunten te implementeren:
GET /api/websites/:websiteId/pageviews- Zoals de naam suggereertM SK1 geeft dit eindpunt terug de paginaaanzichten en 'sessies' voor een bepaalde website over een tijdsbestek.
{
"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- dit gee terug de basis statistieken van een bepaalde 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- dit geeft terug de metingen voor een bepaalde website bu URL, etc.
[
{ "x": "/", "y": 46 },
{ "x": "/docs", "y": 17 },
{ "x": "/download", "y": 14 }
]
Zoals je kan zien uit de documenten, aanvaarden ze allemaal een aantal parameters ( en ik heb deze als zoekparameters in de code onder representaeerd.
Testen in Rider http-kliënt
Ik begin altijd met het testen van de API in Rider.
### 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}}
Het is een goede praktijk om de variabele namen hier in te houden. {{}} Een env.json-lêer waar je naar kan verwijzen zoals onder
{
"local": {
"umamiurl":"umamilocal.mostlylucid.net",
"username": "admin",
"password": "<password{>",
"websiteid" : "32c2aa31-b1ac-44c0-b8f3-ff1f50403bee"
}
}
Opstelling
Eerst moeten we HttpClient konfigureren en de diensten die we gebruiken om de verzoeken te doen.
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 konfigureren we de instellingsklas. UmamiSettings en voeg de AuthService en UmamiService We voegen ook een herprobeerbeleid toe aan de HttpClient om tijdelijke fouten te behandelen.
Vervolgens moeten we de UmamiService en AuthService klasse.
De gemeenschappelijke wetgeving van het land. AuthService is simpelweg verantwoordelijk voor het krijgen van de JWT-token van de 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 hebben we een eenvoudige methode. LoginAsync Die stuurt een POST-vraag naar de /api/auth/login Endpunt met gebruikernaam en wachtwoord. Als de vraag succesvol is, slaan we het JWT-token in de _token Het veld en de instelling. Authorization header op de HttpClient.
De gemeenschappelijke wetgeving van het land. UmamiService is verantwoordelijk voor het doen van de verzoeken aan de API.
Voor elke van de belangrijkste methodes heb ik de vraag-objecten gedefinieërd die alle parameters voor elk eindpunt accepteren.
Ze volgen allemaal een simiiar patroon, dus ik zal er één laten zien.
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 zie je dat ik het verzoekobject neem.
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
}
En bouwen de zoek String van de parameters. Als het verzoek succesvol is, geven we de inhoud terug als een UmamiResult object. Als de vraag niet lukt met een 401 statuscodeM SK2 roepen we de LoginAsync methode en herproberen van de vraag. Dit zorgt ervoor dat we 'elegantly ' het tekenverstreken behandelen
Conclusie
Dit is een eenvoudig voorbeeld van hoe je een C-client maakt voor de Umami API.