I am trying to create a weather Nuget package that can be used in any .net core project, the library should give me the current weather for a specific city that I chose in Celsius or Fahrenheit.
I will use the weatherapi.com to get the current weather.
The library should support the dependency injection (DI) throw a service called IGetCurrentWeather with one method that’s take a city name and optional parameter to chose whether I want the result in Celsius or Fahrenheit.
I should be able to register the library in the startup of any .NET CORE project and provide the necessary configurations like the Code below
builder.Services.AddWeatherApi(WeatherOptions=>
WeatherOptions.ApiKey= "d1398cba8a894feb9f7180821232602");
the interface (the apikey below is for example):
public interface IGetCurrentWeather
{
public async Task<string> GetCurrentWeather()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://api.weatherapi.com/v1/current.json?key=d1398cba8a894feb9f7180821232602&q=London&aqi=no\r\n");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
}
AddWeather Function:
public static class WeatherApi
{
public static WeatherOptions Options { get; } //set only via Secret Manager
public static IServiceCollection AddWeatherApi(this IServiceCollection services, Action<WeatherOptions> configure)
{
services.Configure(configure) ;
return services;
}
WeatherOptions:
public class WeatherOptions
{
public string ApiKey { get; set; }
}
how can i use the ApiKey that i entered in startup.cs when call GetCurrentWeather() function to access weatherapi.com account? Any help highly appreciated.
Your attempted solution here is close. But in general, you should not put an actual implementation in an interface. You can think of an interface as a contract, it defines the operations that you expect any implementing class to be able to perform.
You start off okay with this:
builder.Services.AddWeatherApi(WeatherApiOptions =>
WeatherOptions.ApiKey= "d1398cba8a894feb9f7180821232602");
We should rename your interface, just for consistency's sake. And remove the implementation. So what you used to call IGetCurrentWeather should look like this:
public interface IWeatherApi
{
public Task<string> GetCurrentWeather();
}
Then you need a class that implements the interface. It should receive the WeatherApiOptions via constructor injection:
public class WeatherApi : IWeatherApi
{
readonly WeatherApiOptions _weatherApiOptions;
public WeatherApi(IOptions<WeatherApiOptions> weatherApiOptions)
{
_weatherApiOptions = weatherApiOptions.Value;
}
public async Task<string> GetCurrentWeather()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync($"http://api.weatherapi.com/v1/current.json?key={_weatherApiOptions.ApiKey}&q=London&aqi=no\r\n");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
}
Then, an extension method that helps wire up your class with the DI container. Notice that I renamed it to better describe what its purpose is
public static class WeatherApiDependencyInjectionExtensions
{
public static IServiceCollection AddWeatherApi(this IServiceCollection services, Action<WeatherApiOptions> configure)
{
services.AddTransient<IWeatherApi, WeatherApi>();
services.Configure(configure) ;
return services;
}
}
And rename your options class to make it clearer what its for:
public class WeatherApiOptions
{
public string ApiKey { get; set; }
}
Also, I noticed you're using HttpClient directly. There's some downfalls associated with that. I recommend using Flurl instead.