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

<!--category-- ASP.NET, Umami -->
<datetime class="hidden">2024-08-14T01:27</datetime>

## εισαγωγή

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

Μπορείτε να βρείτε όλο το κώδικα πηγής για αυτό [Το GitHub repo](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid/Umami).

[TOC]

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

Instal Umami. Μπορείτε να βρείτε τις οδηγίες εγκατάστασης [ΕΔΩ](https://www.mostlylucid.net/blog/usingumamiforlocalanalytics) Αυτές οι λεπτομέρειες για το πώς μπορώ να εγκαταστήσω και να χρησιμοποιήσω το Umami για την παροχή αναλύσεων για αυτόν τον ιστότοπο.

Ξανά,Αυτό είναι μια απλή εφαρμογή μερικών από τα καταστήματα της ιστοσελίδας του Umami Stats API endpoints.Μπορείτε να βρείτε την πλήρη τεκμηρίωση API [ΕΔΩ](https://umami.is/docs/api/website-stats).

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

- `GET /api/websites/:websiteId/pageviews` - Όπως υποδηλώνει το όνομαΜSK1 αυτό το τελικό σημείο επιστρέφει τις προβολές σελίδων και Σημεία αναφοράς της 'Η ' για έναν συγκεκριμένο ιστότοπο κατά τη διάρκεια μιας χρονικής περιόδου Η .

```json
{
  "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

```json
{
  "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

```json
[
  { "x": "/", "y": 46 },
  { "x": "/docs", "y": 17 },
  { "x": "/download", "y": 14 }
]
```

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

## Δοκιμή στο Rider HTTPClient

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

```http
### 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 αρχείο το οποίο μπορείτε να αναφέρετε όπως παρακάτω.

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

## Ρυθμίσεις

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

```csharp

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.

```csharp
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 θα δείξω μόνο ένα από αυτά εδώ.

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

```

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

```csharp
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