97 lines
3.9 KiB
C#
97 lines
3.9 KiB
C#
using System.Collections.Specialized;
|
||
using System.Net;
|
||
using System.Net.Http.Headers;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using System.Web;
|
||
using YandexMusic.API.Common;
|
||
using YandexMusic.API.Extensions;
|
||
using YandexMusic.API.Requests.Common.Attributes;
|
||
|
||
namespace YandexMusic.API.Requests.Common;
|
||
|
||
/// <summary>Базовый строитель HTTP-запросов к API Яндекс.Музыки.</summary>
|
||
/// <typeparam name="TResponse">Тип ответа.</typeparam>
|
||
/// <typeparam name="TParams">Тип параметров запроса.</typeparam>
|
||
public abstract class YRequestBuilder<TResponse, TParams>
|
||
{
|
||
private readonly YRequestAttribute _requestInfo;
|
||
private Dictionary<string, string> _substitutions = null!;
|
||
private readonly JsonSerializerOptions _jsonOptions;
|
||
|
||
protected readonly YandexMusicApi api;
|
||
protected readonly AuthStorage storage;
|
||
protected string device;
|
||
|
||
protected YRequestBuilder(YandexMusicApi yandex, AuthStorage auth)
|
||
{
|
||
_requestInfo = GetType().GetCustomAttribute<YRequestAttribute>()
|
||
?? throw new NotImplementedException($"Отсутствует атрибут {nameof(YRequestAttribute)}");
|
||
api = yandex;
|
||
storage = auth;
|
||
device = $"os=CSharp; os_version=; manufacturer=FrigaT; model=Yandex Music API; clid=; device_id={storage.DeviceId}; uuid=random";
|
||
|
||
_jsonOptions = new JsonSerializerOptions
|
||
{
|
||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
|
||
};
|
||
}
|
||
|
||
private Uri BuildUri(TParams tuple)
|
||
{
|
||
var queryParams = GetQueryParams(tuple);
|
||
var modifiedParams = HttpUtility.ParseQueryString(string.Empty);
|
||
foreach (string? key in queryParams)
|
||
if (key != null)
|
||
modifiedParams[key] = ReplaceSubs(queryParams[key]!);
|
||
var endpoint = ReplaceSubs(_requestInfo.Url);
|
||
var builder = new UriBuilder(endpoint) { Query = modifiedParams.ToString() ?? string.Empty };
|
||
return builder.Uri;
|
||
}
|
||
|
||
private HttpRequestMessage CreateMessage(TParams tuple)
|
||
{
|
||
var msg = new HttpRequestMessage
|
||
{
|
||
RequestUri = BuildUri(tuple),
|
||
Method = new HttpMethod(_requestInfo.Method),
|
||
Content = GetContent(tuple)
|
||
};
|
||
msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptCharset.GetName(), Encoding.UTF8.WebName);
|
||
msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptEncoding.GetName(), "gzip");
|
||
if (!string.IsNullOrEmpty(storage.Token))
|
||
msg.Headers.TryAddWithoutValidation(HttpRequestHeader.Authorization.GetName(), $"OAuth {storage.Token}");
|
||
SetCustomHeaders(msg.Headers);
|
||
return msg;
|
||
}
|
||
|
||
protected string ReplaceSubs(string str)
|
||
{
|
||
var subs = str.GetMatches(@"\{.+?\}");
|
||
foreach (var s in subs)
|
||
{
|
||
var key = s.ReplaceRegex(@"[\{\}]", string.Empty);
|
||
if (!_substitutions.TryGetValue(key, out var value))
|
||
throw new Exception($"Не найдена подстановка {s}");
|
||
str = str.Replace(s, value);
|
||
}
|
||
return str;
|
||
}
|
||
|
||
protected virtual Dictionary<string, string> GetSubstitutions(TParams tuple) => [];
|
||
protected virtual NameValueCollection GetQueryParams(TParams tuple) => [];
|
||
protected virtual HttpContent? GetContent(TParams tuple) => null;
|
||
protected virtual void SetCustomHeaders(HttpRequestHeaders headers) { }
|
||
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
|
||
|
||
internal YRequest<TResponse> Build(TParams tuple)
|
||
{
|
||
_substitutions = GetSubstitutions(tuple);
|
||
var msg = CreateMessage(tuple);
|
||
return new YRequest<TResponse>(msg, api, storage);
|
||
}
|
||
} |