-
Notifications
You must be signed in to change notification settings - Fork 0
Requests and URI Builder RU
Эта страница описывает три записи-запроса (LogsQuery, AdminActivityQuery, TopOperationsQuery) и статический
класс LogsRequestUriBuilder, который превращает их в относительные строки URI, а также все правила проверки и
нормализации, применяемые построителем.
Все три записи находятся в пространстве имён LogsParser.Models и являются запечатанными неизменяемыми записями.
public sealed record LogsQuery(
int ServerId,
IReadOnlyCollection<string>? Filters = null,
DateTime? PeriodFrom = null,
DateTime? PeriodTo = null,
string? Player = null,
string? Target = null,
IPAddress? IpAddress = null,
IReadOnlyDictionary<string, string>? AdditionalParameters = null,
int Page = 1,
int Limit = 1000,
string Sort = "desc");| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
ServerId |
int |
— (обязательный) | Номер игрового сервера. Выводится как server_number. Должен быть больше нуля. |
Filters |
IReadOnlyCollection<string>? |
null |
Коды типов логов. Каждое непустое значение выводится отдельной записью type%5B%5D=. |
PeriodFrom |
DateTime? |
null |
Начало периода. Выводится как min_period; при null опускается. |
PeriodTo |
DateTime? |
null |
Конец периода. Выводится как max_period; при null опускается. |
Player |
string? |
null |
Ник действующего игрока. Выводится как player; опускается при null, пустой строке или пробелах. |
Target |
string? |
null |
Ник игрока-цели. Выводится как target; опускается при null, пустой строке или пробелах. |
IpAddress |
IPAddress? |
null |
Фильтр по IP. Выводится как ip через IPAddress.ToString(); при null опускается. |
AdditionalParameters |
IReadOnlyDictionary<string, string>? |
null |
Параметры dynamic[n], специфичные для фильтра. Ключи должны соответствовать ^dynamic\[\d+\]$. |
Page |
int |
1 |
Номер страницы. Выводится как page. Должен быть больше нуля. |
Limit |
int |
1000 |
Число строк на странице. Выводится как limit после приведения к 100, 500 или 1000. |
Sort |
string |
"desc" |
Порядок сортировки. Допустимы только "desc" и "asc", без учёта регистра. |
Filters и AdditionalParameters — единственные коллекции среди членов записи, и обе необязательны: null
означает просто «такого параметра нет в строке запроса».
using System.Net;
using LogsParser.Models;
var query = new LogsQuery(
ServerId: 18,
Filters: ["connect", "kill"],
PeriodFrom: new DateTime(2026, 5, 1, 3, 0, 0),
PeriodTo: new DateTime(2026, 6, 1, 3, 0, 0),
Player: "Ivan",
IpAddress: IPAddress.Parse("127.0.0.1"),
Page: 2,
Limit: 500);public sealed record AdminActivityQuery(DateTime PeriodFrom, DateTime PeriodTo);| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
PeriodFrom |
DateTime |
— (обязательный) | Начало отчётного периода. Выводится как min_period. |
PeriodTo |
DateTime |
— (обязательный) | Конец отчётного периода. Выводится как max_period. |
Оба члена не допускают null, поэтому оба параметра запроса присутствуют всегда. Построитель не проверяет, что
PeriodFrom предшествует PeriodTo.
using LogsParser.Models;
var query = new AdminActivityQuery(
new DateTime(2026, 5, 1, 3, 0, 0),
new DateTime(2026, 6, 1, 3, 0, 0));public sealed record TopOperationsQuery(string? Filter = null, DateTime? Date = null);| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
Filter |
string? |
null |
Код типа операции. Выводится как type; опускается при null, пустой строке или пробелах. |
Date |
DateTime? |
null |
Дата отчёта. Выводится как date; при null опускается. |
Оба члена необязательны. Если оба остаются null, построитель возвращает голую строку top?.
using LogsParser.Models;
var query = new TopOperationsQuery("transfer", new DateTime(2026, 6, 1));public static partial class LogsRequestUriBuilderПространство имён — LogsParser (корневое, не LogsParser.Models). Класс объявлен partial, потому что проверка
ключей dynamic[n] использует сгенерированный исходниками метод с атрибутом [GeneratedRegex].
Все три метода возвращают относительные строки URI, предназначенные для ParserRequest.RelativeUri, которые
разрешаются относительно базового URI источника данных (по умолчанию https://arizonarp.logsparser.info/). Формы
результата различаются:
| Метод | Форма результата | Пример |
|---|---|---|
BuildLogsUri |
начинается с ?
|
?server_number=18&sort=desc&limit=1000&page=1 |
BuildAdminActivityUri |
начинается с /admins?
|
/admins?min_period=…&max_period=… |
BuildTopOperationsUri |
начинается с top? (без ведущего слеша) |
top?type=transfer&date=… |
public static string BuildLogsUri(LogsQuery query)Строит строку запроса логов. Параметры выводятся в фиксированном порядке: server_number, sort, limit,
page, затем все записи type%5B%5D, затем min_period, max_period, player, target, ip, и в конце
записи AdditionalParameters в порядке перечисления самого словаря.
Проверки выполняются в том же порядке, поэтому о некорректном Sort сообщается раньше, чем о некорректном
Limit, а о некорректном Limit — раньше, чем о некорректном Page.
| Исключение | Когда |
|---|---|
ArgumentNullException |
query равен null. |
ArgumentOutOfRangeException |
ServerId равен нулю или отрицателен; Limit равен нулю или отрицателен; Page равен нулю или отрицателен. |
ArgumentException |
Sort не равен "desc" или "asc"; ключ в AdditionalParameters совпадает с зарезервированным или не соответствует ^dynamic\[\d+\]$. |
using LogsParser;
using LogsParser.Models;
var uri = LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(ServerId: 18));
// ?server_number=18&sort=desc&limit=1000&page=1public static string BuildAdminActivityUri(AdminActivityQuery query)Строит URI активности администраторов. Обе даты выводятся всегда, поэтому результат всегда содержит два параметра.
| Исключение | Когда |
|---|---|
ArgumentNullException |
query равен null. |
using LogsParser;
using LogsParser.Models;
var uri = LogsRequestUriBuilder.BuildAdminActivityUri(new AdminActivityQuery(
new DateTime(2026, 5, 1, 3, 0, 0),
new DateTime(2026, 6, 1, 3, 0, 0)));
// /admins?min_period=2026-05-01%2003%3A00%3A00&max_period=2026-06-01%2003%3A00%3A00public static string BuildTopOperationsUri(TopOperationsQuery query)Строит URI топа операций. Filter выводится как type, а Date — как date; оба пропускаются, если отсутствуют.
| Исключение | Когда |
|---|---|
ArgumentNullException |
query равен null. |
using LogsParser;
using LogsParser.Models;
var uri = LogsRequestUriBuilder.BuildTopOperationsUri(
new TopOperationsQuery("transfer", new DateTime(2026, 6, 1)));
// top?type=transfer&date=2026-06-01%2000%3A00%3A00Должен быть больше нуля. Ноль или отрицательное значение вызывает ArgumentOutOfRangeException с сообщением
ServerId must be greater than zero. Это первая проверка после проверки на null.
// выбрасывает ArgumentOutOfRangeException
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(0));Должен быть больше нуля. Ноль или отрицательное значение вызывает ArgumentOutOfRangeException с сообщением
page must be greater than zero. Любое положительное значение передаётся без изменений.
// выбрасывает ArgumentOutOfRangeException
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Page: 0));Сайт поддерживает ровно три размера страницы — 100, 500 и 1000, — поэтому любое другое положительное значение
округляется вниз до ближайшего поддерживаемого. Ноль или отрицательное значение вызывает
ArgumentOutOfRangeException с сообщением limit must be greater than zero.
Входной Limit
|
Выводимый limit
|
|---|---|
<= 0 |
выбрасывает ArgumentOutOfRangeException
|
1 … 99
|
100 |
100 |
100 |
101 … 499
|
100 |
500 |
500 |
501 … 999
|
500 |
1000 |
1000 |
> 1000 |
1000 |
Кратко: всё, что меньше 500, становится 100; всё, что меньше 1000, становится 500; 1000 и больше становится 1000; значения 100, 500 и 1000 проходят без изменений.
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Limit: 250)); // …&limit=100&…
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Limit: 600)); // …&limit=500&…Допустимы только "desc" (сначала новые) и "asc" (сначала старые). Сравнение выполняется без учёта регистра, а
значение приводится к нижнему регистру перед записью в строку запроса, поэтому "ASC" даёт sort=asc. Всё
остальное — включая пустую строку, строку из пробелов и null — вызывает ArgumentException с сообщением
Sort '<значение>' is not supported. Use 'desc' (newest first) or 'asc' (oldest first).
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Sort: "ASC")); // …&sort=asc&…
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Sort: "descending")); // выбрасывает ArgumentExceptionКаждый фильтр выводится отдельной повторяющейся записью с заранее закодированным ключом type[]:
type%5B%5D=<значение>. Записи, равные null, пустые или состоящие из пробелов, молча пропускаются; само
значение кодируется для URL.
var uri = LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Filters: ["connect", "kill"]));
// содержит type%5B%5D=connect и type%5B%5D=killPeriodFrom, PeriodTo и Date форматируются через
ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), после чего результат кодируется для URL: пробел
становится %20, а двоеточия — %3A. Преобразование часового пояса не выполняется — DateTime используется
ровно в том виде, в каком он передан.
new DateTime(2026, 5, 1, 3, 0, 0) // → min_period=2026-05-01%2003%3A00%3A00Записи, у которых ключ или значение равны null, пусты или состоят из пробелов, молча пропускаются. Каждый
оставшийся ключ проверяется в два этапа, и оба выбрасывают ArgumentException:
-
Конфликт с зарезервированным ключом. Ключ не должен совпадать ни с одним параметром, который построитель формирует сам. Набор зарезервированных ключей сравнивается через
StringComparer.OrdinalIgnoreCaseи содержит ровно:server_number,sort,limit,page,type[],type,min_period,max_period,player,target,ipСообщение:
Additional parameter '<ключ>' conflicts with a reserved logs query parameter. -
Форма ключа. Ключ должен соответствовать регулярному выражению
^dynamic\[\d+\]$(сопоставление без учёта регистра). Сообщение:Additional parameter '<ключ>' is invalid. Only dynamic[n] parameters from the logs page are supported.
Ключ и значение кодируются для URL, поэтому dynamic[0] записывается как dynamic%5B0%5D.
var uri = LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(
18,
AdditionalParameters: new Dictionary<string, string> { ["dynamic[0]"] = "42" }));
// содержит dynamic%5B0%5D=42
// выбрасывает ArgumentException — зарезервированный ключ
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(
18,
AdditionalParameters: new Dictionary<string, string> { ["player"] = "x" }));
// выбрасывает ArgumentException — ключ не вида dynamic[n]
LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(
18,
AdditionalParameters: new Dictionary<string, string> { ["foo"] = "x" }));Ключи dynamic[n] не произвольны: каждый принадлежит конкретному фильтру логов и публикуется самим сайтом в
разметке страницы логов. До вас они доходят через каталог фильтров как
LogsFilterCatalog.Filters[].AdditionalParameters[].QueryKey:
public sealed record LogsFilterCatalog(
IReadOnlyList<LogsFilterDefinition> Filters,
LogsAccount? Account = null);public sealed record LogsFilterDefinition(
string Code,
string Name,
IReadOnlyList<LogsFilterAdditionalParameter> AdditionalParameters);public sealed record LogsFilterAdditionalParameter(
string QueryKey,
string Label);QueryKey — это и есть строка dynamic[n], используемая как ключ словаря, а Label — понятная человеку подпись,
которую сайт показывает рядом с полем ввода. Прочитайте каталог один раз, выберите нужный фильтр и передайте его
QueryKey напрямую в LogsQuery.AdditionalParameters:
using LogsParser;
using LogsParser.Models;
var catalog = await client.GetLogsFilterCatalogAsync();
var filter = catalog.Filters.First(definition => definition.Code == "transfer");
var extra = filter.AdditionalParameters.First(parameter => parameter.Label.Contains("сумма"));
var page = await client.GetLogsAsync(new LogsQuery(
ServerId: 18,
Filters: [filter.Code],
AdditionalParameters: new Dictionary<string, string> { [extra.QueryKey] = "1000000" }));Поскольку ключи берутся с живой страницы, у фильтра без дополнительных полей ввода список AdditionalParameters
просто пуст — коллекция никогда не равна null.
using System.Net;
using LogsParser;
using LogsParser.Models;
var query = new LogsQuery(
ServerId: 18,
Filters: ["connect", "kill"],
PeriodFrom: new DateTime(2026, 5, 1, 3, 0, 0),
PeriodTo: new DateTime(2026, 6, 1, 3, 0, 0),
Player: "Ivan",
IpAddress: IPAddress.Parse("127.0.0.1"),
Page: 2,
Limit: 600,
Sort: "ASC");
Console.WriteLine(LogsRequestUriBuilder.BuildLogsUri(query));Вывод:
?server_number=18&sort=asc&limit=500&page=2&type%5B%5D=connect&type%5B%5D=kill&min_period=2026-05-01%2003%3A00%3A00&max_period=2026-06-01%2003%3A00%3A00&player=Ivan&ip=127.0.0.1
В одной этой строке видны три нормализации: Sort: "ASC" приведён к нижнему регистру и стал sort=asc,
Limit: 600 округлён вниз до limit=500, а обе даты отформатированы по инвариантной культуре и закодированы для
URL. Target остался null, поэтому параметра target в выводе нет вовсе.
LogsRequestUriBuilder — статический класс из чистых функций: он принимает запись, возвращает строку и никогда не
открывает соединение, не трогает cookie и не читает конфигурацию. Благодаря этому его можно использовать
самостоятельно — чтобы записать в лог точный URI, который сформирует вызов, для модульных тестов или для
построения запросов, которые вы собираетесь выполнить собственным HTTP-стеком вместо LogsParserHttpDataSource.
using LogsParser;
using LogsParser.Models;
var relativeUri = LogsRequestUriBuilder.BuildLogsUri(new LogsQuery(18, Limit: 100));
var absolute = new Uri(new Uri("https://arizonarp.logsparser.info/"), relativeUri);
Console.WriteLine(absolute);Ровно эту же строку LogsParserClient передаёт источнику данных в виде ParserRequest.RelativeUri:
public sealed record ParserRequest(string RelativeUri, ICookieStorage? CookieStorage = null);- LogsParserClient — фасад, который вызывает эти построители за вас
- Модели — записи-ответы, которые возвращают эти запросы
- Парсеры — превращение полученного HTML в модели без HTTP
-
Транспорт и аутентификация — как выполняется
RelativeUri - Начало работы — первый запрос от начала до конца
- Главная
LogsParser · CC BY-NC 4.0 — non-commercial use only / только некоммерческое использование
English
- Home
- Getting Started
- LogsParserClient
- Requests and URI Builder
- Models
- Parsers
- Transport and Authentication
- Cookie Storage
- Dependency Injection
- Logging
- Exceptions
Русский
- Главная
- Начало работы
- LogsParserClient
- Запросы и построитель URI
- Модели
- Парсеры
- Транспорт и авторизация
- Хранение cookies
- Внедрение зависимостей
- Логирование
- Исключения