Я пытаюсь реализовать универсальный вызывающий объект, который использует различные погодные API OpenWeatherMap, но я застрял в связи с тем, как ввести правильный идентификатор для ссылки.
- .../weather?q=... возвращает данные JSON для текущей погоды;
- ... / прогноз? q = ... возвращает данные JSON для пятипрогноз на день.
Я ищу способ из учебника, возможно, получить тип API каждого класса путем доступа к GetAPIType()
, привести его к типу int и поместить его в индекс, чтобы я могбыть в состоянии использовать identifiers[index]
.Или, возможно, есть более простой способ сделать это.
Проверка на typeof(T)
также приходила мне в голову, и я назначал бы индекс в зависимости от конструкции if(typeof(T).Equals(typeof(...)))
, но это выглядит очень грязно, и если бы у OpenWeatherMap было 100 API в теории, мне понадобилось бы 100 различныхесли конструирует.Имея это в виду, разве создание этих проверок не превзойдет цель универсальности Client?
Третье решение, о котором я подумал, это передача APIType type
в качестве параметра для конструктора Client,
например var client = new Client<CurrentWeatherDTO>(APIType.CurrentWeather, location, apiKey)
,
, но учитывая тот факт, что Client является универсальным, и я уже предоставляю тип, когда я его создаю, он будет казаться ужасно избыточным.
Client.cs
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Reflection;
namespace Rainy.OpenWeatherMapAPI
{
public class Client<T>
{
private readonly string location;
private readonly string apiKey;
private readonly string requestUri;
private readonly string[] identifiers = { "weather", "forecast" };
private readonly int index;
public Client(string location, string apiKey)
{
// Get the type of API used in order to get the right identifier for the link.
// ??? Maybe use Reflection, somehow.
this.location = location;
this.apiKey = apiKey;
requestUri = $"api.openweathermap.org/data/2.5/{}?q={location}&appid={apiKey}";
}
public async Task<T> GetWeather(CancellationToken cancellationToken)
{
using (var client = new HttpClient())
using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
{
var stream = await response.Content.ReadAsStreamAsync();
if (response.IsSuccessStatusCode)
return DeserializeJsonFromStream<T>(stream);
var content = await StreamToStringAsync(stream);
throw new APIException
{
StatusCode = (int)response.StatusCode,
Content = content
};
}
}
private U DeserializeJsonFromStream<U>(Stream stream)
{
if (stream == null || stream.CanRead == false)
return default(U);
using (var sr = new StreamReader(stream))
using (var jtr = new JsonTextReader(sr))
{
var js = new JsonSerializer();
var searchResult = js.Deserialize<U>(jtr);
return searchResult;
}
}
private async Task<string> StreamToStringAsync(Stream stream)
{
string content = null;
if (stream != null)
using (var sr = new StreamReader(stream))
content = await sr.ReadToEndAsync();
return content;
}
}
}
APIType.cs
namespace Rainy.OpenWeatherMapAPI
{
public enum APIType
{
CurrentWeather = 0,
FiveDayForecast = 1
}
}
IWeather.cs
namespace Rainy.OpenWeatherMapAPI
{
public interface IWeather
{
APIType GetAPIType();
}
}
CurrentWeatherDTO.cs
namespace Rainy.OpenWeatherMapAPI.CurrentWeatherData
{
class CurrentWeatherDTO : IWeather
{
public APIType GetAPIType()
{
return APIType.CurrentWeather;
}
}
}
FiveDayForecastDTO.cs
namespace Rainy.OpenWeatherMapAPI.WeatherForecastData
{
class FiveDayForecastDTO : IWeather
{
public APIType GetAPIType()
{
return APIType.FiveDayForecast;
}
}
}