I'm trying to fetch openweatherapi data and return this data as object, so later in _Layout I can inject this class and use this object. However, in _Layout I can't access to object properties.
public Weather WeatherApiResult()
{
Weather weather = null;
Task t = Task.Factory.StartNew(async () =>
{
string city = Weather();
string apiKey = "KEY";
string URL = "https://api.openweathermap.org/data/2.5/weather?q=" city "&units=metric&appid=" apiKey;
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("apiKey", $"{apiKey}");
var httpResponse = await httpClient.GetAsync($"{URL}");
var response = await httpResponse.Content.ReadAsStringAsync();
weather = JsonConvert.DeserializeObject<Weather>(response);
});
return weather;
}
_Layout:
@{
var weather = settingsService.WeatherApiResult();
weather.name
}
Update:
@{
var response = settingsService.WeatherApiResult();
var name = response.Result.name;
var timezone = response.Result.timezone;
<p>@name</p>
<p>@timezone</p>
}
It looks like I can access only the first-level properties name
, timezone
, id
, cod
. How can I access the nested properties?
In JavaScript I would do response.main.temp for temperature. Do I need to create a Class for each nested item?
CodePudding user response:
The common way is to use view components
Inside of Shared folder you have to crate a Components folder and then _Weather folder. Create the partial view _Weather.cshtml there
@model Weather
<div>
.... your html here like
<p>@Model.name</p>
<p>@Model.timezone</p>
</div>
Create class in a Controllers folder
[ViewComponent(Name = "_Weather")]
public class _WeatherViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
//or
//public IViewComponentResult Invoke()
{
Weather weather = null;
string city = Weather();
string apiKey = "KEY";
..... continue your code
return View("_Weather", weather);
}
_Layout
<div> @(await Component.InvokeAsync("_Weather")) </div>
another way is to creage a BaseModel class with Weather property and each view model should use BaseModel as a base class. And each time you should download and assign data to the Weather property.
And you can also use your settingsService if you need
[ViewComponent(Name = "_Weather")]
public class _WeatherViewComponent : ViewComponent
{
private readonly SettingsService _settingsService;
public _WeatherViewComponent(SettingsService settingsService)
{
_settingService = settingsService;
}
public IViewComponentResult Invoke()
{
var weather = _settingsService.WeatherApiResult();
return View("_Weather", weather);
}
}
CodePudding user response:
After going through additional added details on your question, I am adding another answer which I think should solve your problem. Your actual problem is that you are unable to correctly convert the ApiResponse to C# class object because I am assuming that your C# class is not structured correctly. Create an ApiResponseModel
class or name it anything you like, this class would be equal to your entire Api response. It should look something like this:-
namespace Your.NameSpace
{
public class ApiResponseModel //this class will have the base level properties
{
public Coord coord { get; set; }
public List<Weather> weather { get; set; } //weather is a list because your ApiResponse has an array of Weather objects
public Main main { get; set; }
public Wind wind { get; set; }
public Clouds clouds { get; set; }
}
public class Coord
{
public decimal lon { get; set; }
public decimal lat { get; set; }
}
public class Weather
{
public int id { get; set; }
public string main { get; set; }
public string description { get; set; }
public string icon { get; set; }
}
//similarly add more classes below to be able to correctly convert your json to C# if other properties are required to you.
}
Similar to how I have added classes above, you need to add a class for each nested json object.