Back to "إضافة مراجع C# ل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# لAPI أمامي

Wednesday, 14 August 2024

التقديم

في هذا المقال, سأريكم كيفية إنشاء نموذج C# لAPI إبلاغumamiM SK2 هذا مثال بسيط يوضح كيفية التحقق من الهوية مع الAPI وإستخراج البيانات منه

يمكنك أن تجد كل شفرة المصدر لهذا على موقع GitHub.

المتطلبات الأساسية

Install Umami. يمكنكم العثور على التعليمات установки هنا هذا يوضح كيف أقوم بتركيب و استخدام "أمامي" لإيصال التحليلات لهذا الموقع.

مرة أخرى, هذا تطبيق بسيط لبعض من نقاط نهاية API لبيانات موقع "أمامي" هنا.

في هذا اخترت أن أقوم بتطبيق هذه النقاط النهائية:

  • GET /api/websites/:websiteId/pageviews - كما يقترح اسمه, هذه النقطة النهائية تعيد المشاهد على الصفحة و 'sessionsM SK3 لموقع معين على مدى فترة زمنية
{
  "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 - هذا يعيد إحصائيات أساسية لموقع معينM 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 - هذا يعيد القياسات لموقع معين URL أو ما شابه
[
  { "x": "/", "y": 46 },
  { "x": "/docs", "y": 17 },
  { "x": "/download", "y": 14 }
]

كما ترون من الأبحاث , كل هذه تقبل عدد من العوامل ( وقد قمت بتمثيلها كعوامل بحث في الشفرة التالية

الاختبار في المركبة httpClient

أبدأ دائما باختبار API في Rider's built-in HTTP clientM SK2 هذا يسمح لي باختبار الAPI بسرعة ورؤية الاستجابة

### 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 هو ببساطة مسؤول عن الحصول على توكين 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 التي ترسل طلباً POST إلى /api/auth/login نقطة النهاية مع username و password. إذا كان الطلب ناجحاً, سنخزن رمز JWT في _token وضبط الحقل Authorization عنوان على HttpClient.

الـ UmamiService هو المسؤول عن تقديم الطلبات إلى API. في كل من الطرق الرئيسية، قمت بتحديد أجسام الطلب التي تقبل جميع العوامل لكل نقطة النهاية.

إنهم جميعاً يتبعون نمط سيميير , لذا سأريكم واحد منهم هنا

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 object. إذا فشلت الطلب مع 401 شفرة حالةM SK2 نتصل بالـ LoginAsync طريقة وإعادة تجربة الطلب. هذا يضمن لنا 'élégantlyM SK2 التعامل مع نهاية الرموز

الاستنتاجات

هذا مثال بسيط لكيفية إنشاء عملاء C# لAPI أمامي. يمكنك استخدام هذا كبداية لبناء عملاء أكثر تعقيدا أو دمج الAPI في تطبيقات الخاصة بك

logo

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