我正在尝试实现一个使用OpenWeatherMap的不同天气API的通用调用程序,但在如何为链接输入正确的标识符方面我陷入了困境。
我正在寻找一种教科书方式,也许可以通过访问GetAPIType()
来检索每个类的API类型,并将其转换为int并将其放入索引中,以便能够使用{{1 }}。也许有更简单的方法可以做到这一点。
检查identifiers[index]
也是我的主意,我将根据typeof(T)
构造来分配索引,但这似乎很混乱,如果OpenWeatherMap在理论上有100个API,我将需要100个如果构造不同。考虑到这一点,创建这些支票是否会超越客户通用化的目的?
我想到的第三个解决方案是将if(typeof(T).Equals(typeof(...)))
作为Client构造函数的参数传递,
例如APIType type
,
但是考虑到Client是泛型的,并且我在实例化它时已经提供了一个类型,所以它看起来非常多余。
Client.cs
var client = new Client<CurrentWeatherDTO>(APIType.CurrentWeather, location, apiKey)
APIType.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;
}
}
}
IWeather.cs
namespace Rainy.OpenWeatherMapAPI
{
public enum APIType
{
CurrentWeather = 0,
FiveDayForecast = 1
}
}
CurrentWeatherDTO.cs
namespace Rainy.OpenWeatherMapAPI
{
public interface IWeather
{
APIType GetAPIType();
}
}
FiveDayForecastDTO.cs
namespace Rainy.OpenWeatherMapAPI.CurrentWeatherData
{
class CurrentWeatherDTO : IWeather
{
public APIType GetAPIType()
{
return APIType.CurrentWeather;
}
}
}
答案 0 :(得分:1)
我不会使用枚举来驱动数组的索引。
我将以静态方式直接返回字符串。
如果需要,此解决方案也可以使用数组的索引。
以下是代码和dotnetfiddle:
using System;
public class Program
{
public static void Main()
{
var client1 = new Client<CurrentWeatherDTO>(null);
Console.WriteLine("Client CurrentWeather type: " + client1.Type);
var client2 = new Client<FiveDayForecastDTO>(null);
Console.WriteLine("Client FiveDay type: " + client2.Type);
}
public class Client<T> where T : IWeather, new()
{
public string Type { get; set; }
public Client(string apiKey)
{
var dto = (IWeather)new T();
this.Type = dto.GetAPIType();
}
}
public static class APIType
{
public static string CurrentWeather = "weather";
public static string FiveDayForecast = "forecast";
}
public interface IWeather
{
string GetAPIType();
}
class CurrentWeatherDTO : IWeather
{
public string GetAPIType()
{
return APIType.CurrentWeather;
}
}
class FiveDayForecastDTO : IWeather
{
public string GetAPIType()
{
return APIType.FiveDayForecast;
}
}
}
答案 1 :(得分:1)
我可能会使用这样的解决方案,但可能会处理更多错误。
关于如何使用HttpClient,有一些参考。
我不太了解{}
中requestUri中的部分,也许这是您遇到的问题,我在示例代码中将其更改为{???}
。
class Client
{
// Problems using HttpClient and look into using IHttpClientFactory...
// http://byterot.blogspot.com/2016/07/singleton-httpclient-dns.html
// https://www.hanselman.com/blog/HttpClientFactoryForTypedHttpClientInstancesInASPNETCore21.aspx
static HttpClient _httpClient = new HttpClient();
readonly string WeatherUri = $"api.openweathermap.org/data/2.5/{???}?q={0}&appid={1}";
public async Task<T> GetWeather<T>(string location, CancellationToken cancellationToken)
{
var apiKey = ApiKeyAttribute.GetApiKey<T>();
if (apiKey == null) throw new Exception("ApiKeyAttirbute missing");
var requestUri = string.Format(WeatherUri, location, apiKey);
return await GetItem<T>(requestUri, cancellationToken);
}
public async Task<T> GetItem<T>(string requestUri, CancellationToken cancellationToken)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);
var response = await _httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!response.IsSuccessStatusCode) throw new Exception("Error requesting data");
if (response.Content == null) return default(T);
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(content);
}
}
[ApiKeyAttribute("weather")]
class CurrentWeatherDTO { /* add appropriat properties */ }
[ApiKeyAttribute("forecast")]
class FiveDayForecastDTO { /* add appropriat properties */ }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
class ApiKeyAttribute : Attribute
{
public string Name { get; private set; }
public ApiKeyAttribute(string name)
{
Name = name;
}
public static string GetApiKey<T>()
{
var attribute = typeof(T).GetCustomAttribute<ApiKeyAttribute>();
return attribute?.Name;
}
}