Setting up an N-Tier ASP.NET Core App

Photo by Elena Koycheva on Unsplash

Introduction

Prerequisites

N-Tier architecture

Source: Wikipedia
Source: Microsoft
$ git clone -b 0_GettingStarted --single-branch git@github.com:jsheridanwells/WeatherWalkingSkeleton.git
$ dotnet restore

Getting the OpenWeatherMap API key

api.openweathermap.org/data/2.5/forecast?q={ city name }&appid={ your api key }

Securing our API key

$ mkdir Config
$ touch Config/OpenWeather.cs
namespace WeatherWalkingSkeleton.Config
{
public class OpenWeather
{
public string ApiKey { get; set; }
}
}
$ ~/.microsoft/usersecrets/<USER-SECRETS-ID>/secrets.json
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>65988f0a-26ed-44ef-8749-f86a2f5c18a9</UserSecretsId>
</PropertyGroup>
</Project>
$ dotnet user-secrets set "OpenWeather:ApiKey" "YOUR-API-KEY"
Successfully saved OpenWeather:ApiKey = YOUR-API-KEY to the secret store.
// Startup.cs
using WeatherWalkingSkeleton.Config;

// [...]
public class Startup
{
// [...]
public void ConfigureServices(IServiceCollection services)
{
// Add OpenWeatherMap API key
var openWeatherConfig = Configuration.GetSection("OpenWeather");
services.Configure<OpenWeather>(openWeatherConfig);
// [...]
}

Classes for mapping the OpenWeatherMap API response

$ mkdir Models
$ touch Models/{WeatherForecast.cs,OpenWeatherResponse.cs}
// WeatherForecast.cs

using System;

namespace WeatherWalkingSkeleton.Models
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public decimal Temp { get; set; }
public decimal FeelsLike { get; set; }
public decimal TempMin { get; set; }
public decimal TempMax { get; set; }
}
}
// OpenWeatherResponse.cs

using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace WeatherWalkingSkeleton.Models
{
public class OpenWeatherResponse
{
[JsonPropertyName("list")]
public List<Forecast> Forecasts { get; set; }

}
// [... continue to add here]
}
// OpenWeatherResponse.cs
// [...]
public class Forecast
{
[JsonPropertyName("dt")]
public int Dt { get; set; }
[JsonPropertyName("main")]
public Temps Temps { get; set; }
}
// [...]
// OpenWeatherResponse.cs
// [...]
public class Temps
{
[JsonPropertyName("temp")]
public decimal Temp { get; set; }
[JsonPropertyName("feels_like")]
public decimal FeelsLike { get; set; }
[JsonPropertyName("temp_min")]
public decimal TempMin { get; set; }
[JsonPropertyName("temp_max")]
public decimal TempMax { get; set; }
}
// [...]

Setting up and injecting an ASP.NET Core service

$ mkdir Services
$ touch Services/OpenWeatherService.cs
using System;
using System.Collections.Generic;
using WeatherWalkingSkeleton.Models;

namespace WeatherWalkingSkeleton.Services
{
public enum Unit
{
Metric,
Imperial,
Kelvin
}

public interface IOpenWeatherService
{
List<WeatherForecast> GetFiveDayForecast(string location, Unit unit = Unit.Metric);
}
// [...]
}
public class OpenWeatherService : IOpenWeatherService

public List<WeatherForecast> GetFiveDayForecast(string location, Unit unit = Unit.Metric)
{
return new NotImplementedException();
}
public class Startup
{
// [...]
public void ConfigureServices(IServiceCollection services)
{
// [...]
services.AddScoped<IOpenWeatherService, OpenWeatherService>();
// [...]
}
}
using WeatherWalkingSkeleton.Services;

namespace WeatherWalkingSkeleton.Controllers
{
public class WeatherForecastController : ControllerBase
{

private readonly IOpenWeatherService _weatherService;

public WeatherForecastController(ILogger<WeatherForecastController> logger, IOpenWeatherService weatherService)
{
_logger = logger;
_weatherService = weatherService;
}
// [...]
}
}
[HttpGet]
public IActionResult Get()
{
var forecast = _weatherService.GetFiveDayForecast("Chicago");
return Ok(forecast);
}
}

Implementing our HTTP service

api.openweathermap.org/data/2.5/forecast?q=Chicago&appid=YOUR-API-KEY8&units=imperial
using Microsoft.Extensions.Options;
using WeatherWalkingSkeleton.Config;
// [...]
public class OpenWeatherService : IOpenWeatherService
{
private OpenWeather _openWeatherConfig;

public OpenWeatherService(IOptions<OpenWeather> opts)
{
_openWeatherConfig = opts.Value;
}
// [...]
}
public List<WeatherForecast> GetFiveDayForecast(string location, Unit unit = Unit.Metric)
{
string url = $"https://api.openweathermap.org/data/2.5/forecast?q={ location }&appid={ _openWeatherConfig.ApiKey }&units={ unit }";
// [...]
}
using WeatherWalkingSkeleton.Models;
// [...]
public List<WeatherForecast> GetFiveDayForecast(string location, Unit unit = Unit.Metric)
{
string url = $"https://api.openweathermap.org/data/2.5/forecast?q={ location }&appid={ _openWeatherConfig.ApiKey }&units={ unit }";
var forecasts = new List<WeatherForecast>();

return forecasts;
}
// 0. Use the .NET HttpClient library
using (HttpClient client = new HttpClient())
{
// 1. Make the request
var response = client.GetAsync(url).Result;
var json = response.Content.ReadAsStringAsync().Result;

// 2. Deserialize the response.
var openWeatherResponse = JsonSerializer.Deserialize<OpenWeatherResponse>(json);

// 3. Build the list of forecasts
foreach (var forecast in openWeatherResponse.Forecasts)
{
forecasts.Add(new WeatherForecast
{
Date = new DateTime(forecast.Dt),
Temp = forecast.Temps.Temp,
FeelsLike = forecast.Temps.FeelsLike,
TempMin = forecast.Temps.TempMin,
TempMax = forecast.Temps.TempMax,
});
}
}
public List<WeatherForecast> GetFiveDayForecast(string location, Unit unit = Unit.Metric)
{
string url = $"https://api.openweathermap.org/data/2.5/forecast?q={ location }&appid={ _openWeatherConfig.ApiKey }&units={ unit }";
var forecasts = new List<WeatherForecast>();
using (HttpClient client = new HttpClient())
{
var response = client.GetAsync(url).Result;
var json = response.Content.ReadAsStringAsync().Result;
var openWeatherResponse = JsonSerializer.Deserialize<OpenWeatherResponse>(json);
foreach (var forecast in openWeatherResponse.Forecasts)
{
forecasts.Add(new WeatherForecast
{
Date = new DateTime(forecast.Dt),
Temp = forecast.Temps.Temp,
FeelsLike = forecast.Temps.FeelsLike,
TempMin = forecast.Temps.TempMin,
TempMax = forecast.Temps.TempMax,
});
}
}

return forecasts;
}
public IActionResult Get(string location, Unit unit = Unit.Imperial)
{
var forecast = _weatherService.GetFiveDayForecast(location, unit);
return Ok(forecast);
}
https://localhost:5001/WeatherForecast?location=london&unit=kelvin

Summary

I am a second-career software engineer currently working in the public sector.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store