Fetch the current weather using ASP.NET Core Web API and OpenWeather

January 24, 2017 · 7 minute read

Sometimes you need to access someone else’s API from your web application. Here we’ll take a look at calling the OpenWeather API from a Web API controller in your .NET Core web app.

So far we’ve created an Angular 2 .NET Core web site and started work on a component to show the current weather.

To make life easier we used hardcoded data whilst we focused on building our Angular 2 component and retrieving data from Web API.

Now let’s try using the weather API over at openweathermap.org to display the current weather for any city.

They have a number of API calls you can make and you can get started for free.

Before you do anything else you’re going to need an API key. Go to the Signup page.

Fill in the details and you should be good to go. You’ll find your API key (which you’ll need shortly) on the API keys page (under your account).

Weather controller

Take a look at your weather web api controller. It should look something like this.

using Microsoft.AspNetCore.Mvc;

namespace WeatherChecker.Controllers
{
    [Route("api/[controller]")]
    public class WeatherController : Controller
    {
        [HttpGet("[action]/{city}")]
        public IActionResult City(string city)
        {
            return Ok(new { Temp = "12", Summary = "Barmy", City = city });
        }
    }
}

We’re going to change this to make a call to OpenWeather API to get the current weather for whichever city has been requested.

OpenWeather API Basics

To access the OpenWeather API you simple need to make an HTTP request which includes your api key and any other parameters such as city name.

For example, to get the current weather for London, enter this URL into a browser (include your OpenWeather API key as the app id).

http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=YOUR_API_KEY_HERE

This will return json data like this.

{ "base" : "stations",
  "clouds" : { "all" : 75 },
  "cod" : 200,
  "coord" : { "lat" : 51.509999999999998,
      "lon" : -0.13
    },
  "dt" : 1485370200,
  "id" : 2643743,
  "main" : { "humidity" : 93,
      "pressure" : 1026,
      "temp" : 274.31999999999999,
      "temp_max" : 276.14999999999998,
      "temp_min" : 272.14999999999998
    },
  "name" : "London",
  "sys" : { "country" : "GB",
      "id" : 5091,
      "message" : 0.0054000000000000003,
      "sunrise" : 1485330448,
      "sunset" : 1485362355,
      "type" : 1
    },
  "visibility" : 3800,
  "weather" : [ { "description" : "mist",
        "icon" : "50n",
        "id" : 701,
        "main" : "Mist"
      },
      { "description" : "light intensity drizzle",
        "icon" : "09n",
        "id" : 300,
        "main" : "Drizzle"
      },
      { "description" : "fog",
        "icon" : "50n",
        "id" : 741,
        "main" : "Fog"
      },
      { "description" : "haze",
        "icon" : "50n",
        "id" : 721,
        "main" : "Haze"
      }
    ],
  "wind" : { "deg" : 140,
      "speed" : 4.0999999999999996
    }
}

Now we need to make our Weather Web API controller make that same request and return the weather information to our clients (in this case our Angular 2 front-end).

Call the OpenWeather API from code

You’ll need to bring in a few namespaces so add these using statements to your controller.

using System.Net.Http;
     using System.Threading.Tasks;
     using Newtonsoft.Json;
     using System.Collections.Generic;
     using System.Linq;

Then modify your City Action method as follows…

[HttpGet("[action]/{city}")]
public async Task<IActionResult> City(string city)
{
    using (var client = new HttpClient())
    {
        try
        {
            client.BaseAddress = new Uri("http://api.openweathermap.org");
            var response = await client.GetAsync($"/data/2.5/weather?q={city}&appid=YOUR_API_KEY_HERE&units=metric");
            response.EnsureSuccessStatusCode();

            var stringResult = await response.Content.ReadAsStringAsync();
            var rawWeather = JsonConvert.DeserializeObject<OpenWeatherResponse>(stringResult);
            return Ok(new {
                Temp = rawWeather.Main.Temp,
                Summary = string.Join(",", rawWeather.Weather.Select(x=>x.Main)),
                City = rawWeather.Name
            });
        }
        catch (HttpRequestException httpRequestException)
        {
            return BadRequest($"Error getting weather from OpenWeather: {httpRequestException.Message}");
        }
    }
}

There’s quite a lot going on here so let’s unpack it.

Make the controller action asynchronous

public async Task<IActionResult> City(string city)

The first thing to note is that we’ve switched our City action from being synchronous to an asynchronous method. This is good practice when we’re making a potentially slow-returning request to an external web site (as we are here). Doing this asynchronously empowers our server to manage its resources better, freeing up resources that would otherwise be locked until our slow-running request came back with a response.

Call the OpenWeather API and ensure the call completed OK

client.BaseAddress = new Uri("http://api.openweathermap.org");
	var response = await client.GetAsync($"/data/2.5/weather?q={city}&appid=YOUR_API_KEY_HERE&units=metric");
	response.EnsureSuccessStatusCode();

HttpClient lets us make web requests from our .NET (C#) Code. To do so it needs to know which url to make the request to.

This is done in two parts, first we point the BaseAddress property to the Open Weather site (http://api.openweathermap.org) then we pass the rest of the url when we make the call to client.GetAsync.

Note I’ve specified that the response should return metric units (e.g. celsius for temperatures) but you can change that to imperial if it makes more sense for your country.

By calling response.EnsureSuccessStatusCode() we can be sure that the request has come back OK from OpenWeather. If we get an error back from OpenWeather an HttpRequestException will be thrown (which we catch a little later).

Get the response from OpenWeather and deserialize it into a C# object

var stringResult = await response.Content.ReadAsStringAsync();
     var rawWeather = JsonConvert.DeserializeObject<OpenWeatherResponse>(stringResult);

Because we’re in an async method we can use the await keyword. This signals to the server to release the thread it’s allocated to our request. This effectively frees up some of our server’s resources (that it would otherwise hang on to), and makes them available for another request.

Once the call to OpenWeather comes back, the server will allocate a thread and get on with the job of processing the response.

In this case we use Newtonsoft Json.NET to deserialize the json response from OpenWeather into an instance of an object. We’ll look at the OpenWeatherResponse class shortly.

Return a trimmed version of the weather data

return Ok(new {
		Temp = rawWeather.Main.Temp,
		Summary = string.Join(",", rawWeather.Weather.Select(x=>x.Main)),
		City = rawWeather.Name
	});

At last we can return our weather data. We don’t really want (or need) to return the entire OpenWeather response here.

If we did, we’d be passing a fairly complicated object (whose definition we don’t own) to any clients calling our API. Instead we return a simplified response which simply has three properties on it (temperature, summary and city).

ASP.NET Core controllers have a handy Ok helper method which will return whatever data you pass it with a OK (200) response status code.

Handle any errors raised making the call to OpenWeather

catch (HttpRequestException httpRequestException)
	{
		return BadRequest($"Error getting weather from OpenWeather: {httpRequestException.Message}");
	}

Finally we catch any HTTP Request exceptions and return a Bad Request (with an error message). This is similar to the Ok method but instead of an OK (200) status code it returns a Bad Request (400) response.

So what about that OpenWeatherResponse object?

In the City action we took the OpenWeather response and deserialized it into an object. Here’s the classes you’ll need to make that work.

public class OpenWeatherResponse
    {
        public string Name { get; set; }

        public IEnumerable<WeatherDescription> Weather { get; set; }

        public Main Main { get; set; }
    }

    public class WeatherDescription
    {
        public string Main { get; set; }
        public string Description { get; set; }
    }

    public class Main
    {
        public string Temp { get; set; }
    }

JSON.net will map the json values from the OpenWeather response to the relevant properties as defined here.

Note that the response actually includes an array of weather descriptions. Because there is more than one, our controller action uses String.Join to create a combined summary of all the different types of weather currently being experienced in the chosen city.

Test it out

Try hitting http://yoursite/api/weather/city/washington in your browser and you should see the current weather for Washington.

In our next (and final) post in this mini-series we’ll take a look at changing our Angular 2 component to prompt the user for a city (currently it always sends ‘London’ to our API controller).

photo credit: Wouter de Bruijn Trapped via photopin (license)

Join the Practical ASP.NET Newsletter

Ship better Blazor apps, faster. One practical tip every Tuesday.

I respect your email privacy. Unsubscribe with one click.