一. 导言导言 导言 导言 导言
因此,I've 张贴的LOT @-在自己托管的环境中使用Umami进行分析, Umami. Net Nuget Pakakge 网@.#然而我想追踪我的RSS feed;的用户,
问题
问题在于 RSS 种子阅读器试图通过 有用 用户在请求 feed @ .} 时使用代理器 遵守情况 用于追踪用户数量和消费种子的用户类型的提供者@.}然而,}这也意味着Umami将确认这些请求为 立方体 这是需要我使用的一个问题, 因为它导致请求被忽略而不被追踪@.}
Feedbin 用户代理器看起来像此@ : @%
Feedbin feed-id:1234 - 21 subscribers
相当实用的右转@,}它传递一些有用的细节, 说明您的 feed id 是 @,}用户数量和使用者代理 *%.}然而这也是个问题 因为这意味着 Umami 会忽略请求;}事实上 它会返回一个“200”状态 但内容包含 {"beep": "boop"} 意思是这被确定为 bot 请求@. @ this is a bot request as I can '}%t 处理普通错误处理 {(}_it'}s a @200,}不要说“403}等=).}。
当我们在做出这一修改后查看 Umami Dashboard 时, 我现在可以看到原始用户 Agency 请求 feed\ :

这样我们就能看到现在有一堆 触发机器人检测的机器 但是我们现在可以看见这些 然后追踪请求
解决方案
@ ' @ @ maks@ msk0 @ plackage@ MS K1} 我手动分析所有这些请求并检测 Umami是否会将它们检测成一个bot;} 它会使用 IsBot < (\ @ httpsóms://}#www>.npmjs}/}MSQ}MSK8_ package{/isbortMSQK10}来检测请求是否是博特或非博特( MSSK11) 。 @ there @ MPK12}没有 C□#} 等值, 以及它是否正在更改列表, 这样我就可以使用“ MSC15” 甚至将来的列表@(}我可能会变得聪明一些, 并且用此列表检测请求是不是博特或者不是).} 所以我需要截听请求 在它到达乌马米之前 将用户代理 改为U马米会接受的具体要求
所以现在我为我的追踪方法添加了一些额外的参数, 在 Umami.NetMS K1 中这些允许您指定新的“ 'Default用户” 代理@' 将发送到 Umami 而不是原始的用户 Agent. 这让我可以具体说明用户代理人应该更改为特定请求的具体值 @.}
方法和方法
在我的上 UmamiBackgroundSender 我添加了以下内容:
public async Task TrackPageView(string url, string title, UmamiPayload? payload = null,
UmamiEventData? eventData = null, bool useDefaultUserAgent = false)
它存在于所有的跟踪方法上 仅设置一个参数 UmamiPayload 对象@. @%
于 UmamiClient 这些可设定如下: @:}
[Fact]
public async Task TrackPageView_WithUrl()
{
var handler = JwtResponseHandler.Create();
var umamiClient = GetServices(handler);
var response = await umamiClient.TrackPageViewAndDecode("https://example.com", "Example Page",
new UmamiPayload { UseDefaultUserAgent = true });
Assert.NotNull(response);
Assert.Equal(UmamiDataResponse.ResponseStatus.Success, response.Status);
}
在这个测试中,我用的是新的 TrackPageViewAndDecode 返回 UmamiDataResponse 对象@.}% 此对象包含已解码的 JWT 符号 @ ( n如果它是一个bot, 它将无效 。 这样可以检查@ MS K3 和请求的状态=.\ {
PayloadService
这一切都在 Payload 负责弹射有效载荷对象的服务@.}这是 UseDefaultUserAgent 是设置.
默认情况下,我从 HttpContext 所以通常您会得到这个设置的正确@ ; @ I'}稍后将显示它从 Umami_ MS K2 调回到哪个位置Name
private UmamiPayload GetPayload(string? url = null, UmamiEventData? data = null)
{
var httpContext = httpContextAccessor.HttpContext;
var request = httpContext?.Request;
var payload = new UmamiPayload
{
Website = settings.WebsiteId,
Data = data,
Url = url ?? httpContext?.Request?.Path.Value,
IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString(),
UserAgent = request?.Headers["User-Agent"].FirstOrDefault(),
Referrer = request?.Headers["Referer"].FirstOrDefault(),
Hostname = request?.Host.Host
};
return payload;
}
然后,我有一个代码 叫做 PopulateFromPayload 中指定请求对象的位置@' @s data set up:
public static string DefaultUserAgent =>
$"Mozilla/5.0 (Windows 11) Umami.Net/{Assembly.GetAssembly(typeof(UmamiClient))!.GetName().Version}";
public UmamiPayload PopulateFromPayload(UmamiPayload? payload, UmamiEventData? data)
{
var newPayload = GetPayload(data: data);
...
newPayload.UserAgent = payload.UserAgent ?? DefaultUserAgent;
if (payload.UseDefaultUserAgent)
{
var userData = newPayload.Data ?? new UmamiEventData();
userData.TryAdd("OriginalUserAgent", newPayload.UserAgent ?? "");
newPayload.UserAgent = DefaultUserAgent;
newPayload.Data = userData;
}
logger.LogInformation("Using UserAgent: {UserAgent}", newPayload.UserAgent);
}
您将会看到此选项定义了文件“ (” 顶部的新用户代理 。 I'@ t 目前 在方法中检测用户代理是否为无效的 {( 是否应该发生 除非它在没有 HttpContext@)}情况下从代码调用%s UseDefaultUserAgent 设置“. ” 如果设定为默认值,则将用户代理设置到默认值上,并将原始用户代理添加到数据对象@.
然后将此登录为日志, 这样您就可以看到用户代理正在使用什么@ . @
解码回应 .
在Umami.}网@0.3.0}我添加了一些新的方法 返回a UmamiDataResponse 对象@.}此对象包含已解码的 JWT 代号@ .
public async Task<UmamiDataResponse?> TrackPageViewAndDecode(
string? url = "",
string? title = "",
UmamiPayload? payload = null,
UmamiEventData? eventData = null)
{
var response = await TrackPageView(url, title, payload, eventData);
return await DecodeResponse(response);
}
private async Task<UmamiDataResponse?> DecodeResponse(HttpResponseMessage responseMessage)
{
var responseString = await responseMessage.Content.ReadAsStringAsync();
switch (responseMessage.IsSuccessStatusCode)
{
case false:
return new UmamiDataResponse(UmamiDataResponse.ResponseStatus.Failed);
case true when responseString.Contains("beep") && responseString.Contains("boop"):
logger.LogWarning("Bot detected data not stored in Umami");
return new UmamiDataResponse(UmamiDataResponse.ResponseStatus.BotDetected);
case true:
var decoded = await jwtDecoder.DecodeResponse(responseString);
if (decoded == null)
{
logger.LogError("Failed to decode response from Umami");
return null;
}
var payload = UmamiDataResponse.Decode(decoded);
return payload;
}
}
你可以看到,这要求 进入正常 TrackPageView 方法,然后调用一种叫法 DecodeResponse 用于检查应答 beep 和 boop \ ( for bot 检测@). @ 如果找到它们, 它会记录警告并返回 a BotDetected 状态@.}如果能找到它们,它会解码 JWT 符号并返回有效载荷.}
JWT 符号本身只是一个加密字符串, 它包含 Umami 已存储的数据@.}这是解码并返回的 UmamiDataResponse 对象@. @%
此消息的完整来源如下: @:
Response Decoder
using System.IdentityModel.Tokens.Jwt;
namespace Umami.Net.Models;
public class UmamiDataResponse
{
public enum ResponseStatus
{
Failed,
BotDetected,
Success
}
public UmamiDataResponse(ResponseStatus status)
{
Status = status;
}
public ResponseStatus Status { get; set; }
public Guid Id { get; set; }
public Guid WebsiteId { get; set; }
public string? Hostname { get; set; }
public string? Browser { get; set; }
public string? Os { get; set; }
public string? Device { get; set; }
public string? Screen { get; set; }
public string? Language { get; set; }
public string? Country { get; set; }
public string? Subdivision1 { get; set; }
public string? Subdivision2 { get; set; }
public string? City { get; set; }
public DateTime CreatedAt { get; set; }
public Guid VisitId { get; set; }
public long Iat { get; set; }
public static UmamiDataResponse Decode(JwtPayload? payload)
{
if (payload == null) return new UmamiDataResponse(ResponseStatus.Failed);
payload.TryGetValue("visitId", out var visitIdObj);
payload.TryGetValue("iat", out var iatObj);
//This should only happen then the payload is dummy.
if (payload.Count == 2)
{
var visitId = visitIdObj != null ? Guid.Parse(visitIdObj.ToString()!) : Guid.Empty;
var iat = iatObj != null ? long.Parse(iatObj.ToString()!) : 0;
return new UmamiDataResponse(ResponseStatus.Success)
{
VisitId = visitId,
Iat = iat
};
}
payload.TryGetValue("id", out var idObj);
payload.TryGetValue("websiteId", out var websiteIdObj);
payload.TryGetValue("hostname", out var hostnameObj);
payload.TryGetValue("browser", out var browserObj);
payload.TryGetValue("os", out var osObj);
payload.TryGetValue("device", out var deviceObj);
payload.TryGetValue("screen", out var screenObj);
payload.TryGetValue("language", out var languageObj);
payload.TryGetValue("country", out var countryObj);
payload.TryGetValue("subdivision1", out var subdivision1Obj);
payload.TryGetValue("subdivision2", out var subdivision2Obj);
payload.TryGetValue("city", out var cityObj);
payload.TryGetValue("createdAt", out var createdAtObj);
return new UmamiDataResponse(ResponseStatus.Success)
{
Id = idObj != null ? Guid.Parse(idObj.ToString()!) : Guid.Empty,
WebsiteId = websiteIdObj != null ? Guid.Parse(websiteIdObj.ToString()!) : Guid.Empty,
Hostname = hostnameObj?.ToString(),
Browser = browserObj?.ToString(),
Os = osObj?.ToString(),
Device = deviceObj?.ToString(),
Screen = screenObj?.ToString(),
Language = languageObj?.ToString(),
Country = countryObj?.ToString(),
Subdivision1 = subdivision1Obj?.ToString(),
Subdivision2 = subdivision2Obj?.ToString(),
City = cityObj?.ToString(),
CreatedAt = createdAtObj != null ? DateTime.Parse(createdAtObj.ToString()!) : DateTime.MinValue,
VisitId = visitIdObj != null ? Guid.Parse(visitIdObj.ToString()!) : Guid.Empty,
Iat = iatObj != null ? long.Parse(iatObj.ToString()!) : 0
};
}
}
public Guid Id { get; set; }
public Guid WebsiteId { get; set; }
public string? Hostname { get; set; }
public string? Browser { get; set; }
public string? Os { get; set; }
public string? Device { get; set; }
public string? Screen { get; set; }
public string? Language { get; set; }
public string? Country { get; set; }
public string? Subdivision1 { get; set; }
public string? Subdivision2 { get; set; }
public string? City { get; set; }
public DateTime CreatedAt { get; set; }
public Guid VisitId { get; set; }
public long Iat { get; set; }
在结论中的结论
所以只要一个包含 Umami.Net {0.4.0}中一些新功能的短位, 它允许您指定特定请求的默认用户代理@ ._ 这对跟踪Umami 否则会忽略\ MS K3}的请求很有帮助