NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Friday, 13 September 2024
//Less than a minute
I det här inlägget kommer jag att visa dig hur du skapar en C# klient för Umami rapportering API. Detta är ett enkelt exempel som visar hur man autentiserar med API:et och hämtar data från det.
Du kan hitta alla källkoden för detta på min GitHub repo.
Installera Umami. Du hittar installationsanvisningarna här denna information hur jag installerar och använder Umami för att ge analys för denna webbplats.
Återigen, detta är ett enkelt genomförande av några av Umami Website Stats API endpoints. Du hittar den fullständiga API-dokumentationen här.
I detta har jag valt att genomföra följande endpoints:
GET /api/websites/:websiteId/pageviews
- Som namnet antyder, denna endpoint returnerar sidvyer och "sessioner" för en viss webbplats över en tidsperiod.{
"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
- detta returnerar grundläggande statistik för en viss webbplats.{
"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
- detta returnerar mätvärdena för en viss webbplats bu URL etc...[
{ "x": "/", "y": 46 },
{ "x": "/docs", "y": 17 },
{ "x": "/download", "y": 14 }
]
Som ni kan se från dokumenten, dessa alla accepterar ett antal parametrar (och jag har representerat dessa som frågeparametrar i koden nedan).
Jag börjar alltid med att testa API:et i Riders inbyggda HTTP-klient. Detta gör att jag snabbt kan testa API:et och se svaret.
### 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}}
Det är bra att hålla de variabla namnen här i {{}}
en env.json fil som du kan referera till som nedan.
{
"local": {
"umamiurl":"umamilocal.mostlylucid.net",
"username": "admin",
"password": "<password{>",
"websiteid" : "32c2aa31-b1ac-44c0-b8f3-ff1f50403bee"
}
}
Först måste vi konfigurera ut HttpClient och de tjänster vi kommer att använda för att göra förfrågningar.
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)));
}
}
Här ställer vi in inställningsklass UmamiSettings
och lägga till AuthService
och UmamiService
till tjänsteinsamlingen. Vi lägger också till en regresspolicy till HttpClient för att hantera övergående fel.
Nästa vi måste skapa UmamiService
och AuthService
Klasser.
I detta sammanhang är det viktigt att se till att AuthService
är helt enkelt ansvarig för att få JWT token från 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;
}
}
Här har vi en enkel metod LoginAsync
som skickar en POST begäran till /api/auth/login
ändpunkt med användarnamn och lösenord. Om begäran är framgångsrik, lagrar vi JWT token i _token
fält och ställa in Authorization
Rubrik på HttpClienten.
I detta sammanhang är det viktigt att se till att UmamiService
ansvarar för att göra förfrågningar till API:et.
För var och en av de viktigaste metoderna har jag definierat begär objekt som accepterar alla parametrar för varje endpoint. Detta gör det lättare att testa och underhålla koden.
Alla följer samma mönster, så jag ska visa en av dem här.
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);
}
Här kan du se Jag tar begäran objekt
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
}
Och bygga frågesträngen från parametrarna. Om begäran är framgångsrik, returnerar vi innehållet som en UmamiResult
motsätter sig detta. Om begäran misslyckas med en 401 statuskod, ringer vi LoginAsync
metod och försök igen begäran. Detta säkerställer att vi "elegant" hantera token utgång.
Detta är ett enkelt exempel på hur man skapar en C#-klient för Umami API. Du kan använda detta som en utgångspunkt för att bygga mer komplexa kunder eller integrera API i dina egna program.