Consumare una Web API in un APP MVC

Come ricorderete in uno dei miei post precedenti [1] ci eravamo divertiti a creare una Web API con Swagger UI per fornire delle classiche funzionalità CRUD ad un fantomatico database di Fumetti. Ovviamente quella modalità si può applicare a mille oggetti differenti ma, è ora è venuto il momento di consumare l’API in una vera applicazione MVC e vedere come orchestrare il tutto. Per questo esempio farò uso di un’altra API che esporrà un oggetto ServiceTable che, almeno nelle mie intenzioni dovrebbe indicare il tavolo di un locale. Questà entità è molto semplice:

  • ID: è l’identificativo univoco del tavolo (la PK sul DB)
  • Name: è il nome del tavolo, non è obbligatoria ma potrebbe essere utile a chi vuole dare dei nomi a tema… chessò Acqua, Terra, Mare, Fuoco…
  • Seats: è il numero di posti che ha il tavolo
  • Visible: qualora volessimo temporaneamente renderlo indisponibile.
Service Table API

Supponendo quindi di avere già questa API all’interno di un progetto chiamato XinCommonAPI dobbiamo creare la web app che consumerà questa API ed implementerà l’interfaccia grafica (la UI). Aggiungiamo dunque alla soluzione con il progetto contenente la WebAPI un nuovo Progetto

Add new Project

e scegliamo un ASP NET Core Web APP assicurandoci che utilizzi il paradigma MVC

Create MVC Project

Scegliamo quindi come al solito la cartella dove posizionarlo

Choose location

ed infine che tipo di framework vogliamo utilizzare

Framework and Authentication

Alla fine di tutto questo avrò ottenuto il mio nuovo progetto ASP NET Core pronto all’uso

Web App Project

Naturalmente come sempre, prima di fare qualsivoglia modifica il suggerimento è di verificare che i progetti funzioni ed, in questo caso, che funzionino entrambi. Infatti la Web App deve consumare la Web API e quindi entrambi i progetti devono essere lanciati in DEBUG. Per fare questo dobbiamo aprire le proprietà della Solution creata ed impostare entrambi i progetti su Start

Starting Project

Ed in effetti lanciati i due progetti mi ritrovo quello che mi attendevo:

Le due web app

Ok, ora passiamo all’implementazione dell’operazioni CRUD in MVC. Anzitutto io consiglio di crearci una ModelView che rappresenti l’entità che andrà a rappresentare (il nostro ServiceTable) praticamente riproducendo lo stesso tipo di proprietà esposte nell’API.

Creare una classe ModelView che rappresenti l’entità TableService

E questo sarà il codice che immetteremo

public class ServiceTableViewModel
	{
		public int Id { get; set; }
		public string Name { get; set; }
		public int? Seats { get; set; }
		public bool? Visible { get; set; }
	}

Ora passiamo a crearci un controller che vada a lavorare sulla Web API ServiceTable

Creare un Controller vuoto

Che chiameremo ServiceTableController.cs e che conterrà il seguente codice:

public class ServiceTableController : Controller
	{
		Uri baseAddress = new Uri("http://localhost:64853/api/FoodHut");
		HttpClient client;

		public ServiceTableController()
		{
			client = new HttpClient();
			client.BaseAddress = baseAddress;
		}

		public IActionResult Index()
		{
			List<ServiceTableViewModel> modelList = new List<ServiceTableViewModel>();
			HttpResponseMessage response = client.GetAsync(client.BaseAddress + "/ServiceTables").Result;
			if (response.IsSuccessStatusCode)
			{
				string data = response.Content.ReadAsStringAsync().Result;
				modelList = JsonConvert.DeserializeObject<List<ServiceTableViewModel>>(data);
			}
			return View(modelList);
		}
	}

Andiamo a veder nel dettaglio che cosa abbiamo aggiunto nel codice: anzitutto l’URL dell’API ovvero l’endpoint che andremo ad interrogare dove stanno le API. Per l’ambiente di debug lo trovate tra le properties del progetto

URI base of API

Questo Uri sarà quindi utilizzato per inizializzare l’oggetto HttpClient ed invocare la chiamata ottenendo la relativa response. Attenzione che essendo una chiamata Json il risultato va deserializzato facendo uso di un pacchetto Nuget specifico

Newtonsoft.Json library

Il codice in se per se è abbastanza autoesplicativo: si invoca l’API il risultato viene poi deserializzato e convertito nel ModelView relativo. Fate bene attenzione a che il ModelView riporti le properties con lo stesso identico name dell’API altrimenti il deserializzatore non sarà in grado di eseguire il mapping.

Fatto questo non resta che creare la view che visualizzi la lista dei ServiceTable e naturalmente come al solito la autogeneriamo posizionandoci sul metodo Index e quindi generiamo il tutto dal template List

List View

A questo punto è sufficiente lanciare in debug i due progetti ed ecco il risultato:

Lista dei ServiceTable creati

[1] https://www.beren.it/wp-admin/post.php?post=320&action=edit

Catalogo di Fumetti – La prima Web API con Swagger

Sin da piccolo sono sempre stato un grande amante di Fumetti. In effetti ne ho collezionati parecchi, principalmente Marvel, e mi piacerebbe avere una maniera facile di catalogarli e poterci accedere magari anche da dispositivi mobili. Proviamo quindi a creare un app per catalogarli e navigarli. Cominciamo con il definire la struttura dove salvare questi dati: una semplicissima tabella di database (SQL Server) come segue:

  • ID: Identificatore univoco
  • Series: la testata del fumetto
  • Title: il titolo del numero
  • Number: il numero del fumetto all’interno della testata

Partiamo quindi creandoci un catalogo vuoto e definiamo la tabella “Comic” come segue

Comic Table

Apriamo quindi VisualStudio 2019 IDE e creaiamo un nuovo progetto come “Web API Project”

ASP NET Core Web API Template

Chiamerò il progetto XinCataLogSwaggerWebAPI perchè voglio che l’API abbia anche l’interfaccia Swagger

Create Project

Come Framework identifico il 5.0 che è l’ultimo disponibile sulla mi macchina, per il momento lascio Authentication Type a None (ho visto parecchie esempi sul web e viene sempre lasciata a None nel caso servisse ci penseremo)

Framework and Authentication

A questo punto un progetto vuoto con la classe di esempio WeatherForecast è creato per mostrarne il buon funzionamento. Eseguiamo a questo punto il progetto e otteniamo:

Swagger API interface

Abbiamo la pagina Swagger per la nostra WebAPI, certo i metodi non sono che quello di default ma come inizio non è per nulla male.

Aggiungo il progetto a GitHub (lo potete trovare qui [1] se volete curiosarci). Ovviamente il suggerimento come sempre è avere un repository in cui poter salvare modifiche e recuperare i vostri errori nel caso li commettiate. Dato che dobbiamo utilizzare l’EntityFramework per implementare le CRUD da SQL Server aggiungiamo i seguenti pacchetti via NuGet (fate attenzione alla compatibilità con il vostro Framework)

EntityFrameworkCore 5.0.15

La lista finale di pacchetti che dobbiamo installare è la seguente:

NuGet Packages

Ricordo in passato di aver lavorato con un utilissimo wizard per generare il modello dati da un DB SQL con l’approcio Database First, sfortunatamente non sono riuscito a ritrovarlo e sembra che ciò sia dovuto al fatto che con .NET Core non si possono aggiungere oggetti ADO Entity Model [1]. Ad ogni modo è possibile sfruttare un comando dal Package Console Manager che fa qualcosa di similare:

Scaffold-DBContext "Server=WIN-7O15VR47QA6;Database=XinCataLog;Trusted_Connection=True;"  Microsoft.EntityFrameworkCore.SqlServer -OutputDir "Models" -Tables XinComic -f  

Il comando dice quale tabella/entità (in questo caso XinComic) su quale effettuare lo scaffold dell’oggetto e generare la classe model corrispettiva.

Package Manager Command
Models Folder

La classe XinCataLogContext è quella che effettua la comunicazione effettiva con il DB, mentre XinComic è il modello che rappresenta la tabella XinComic nel DB.

Ora passiamo a vedere come esporre le tipiche funzionalità che una API offre: i metodi HTTP di base sono normalmente 4 e si basano sulle operazioni CRUD (GET, PUT, POST, e DELETE vedi anche qui [2]).

  • GET ritorna la risorsa richiesta allo specifico URI. GET on ha effetti sul server (è una lettura).
  • PUT modifica una risorsa esistente allo specifico URI. PUT in alcuni casi può anche supportare la creazione se specificatamente prevista.
  • POST crea una nuova risorsa.
  • DELETE cancella una risorsa relativamente all’URI fornito.

Dato che noi abbiamo già creato un modello negli steps precedenti a cui associare le CRUD non serve fare altro che aggiungere un nuovo Controller alla folder Controller scegliendo specificatamente l’opzione sotto

API Controller with Actions using EntityFramework

Nella finestra modale che si apre non dobbiamo fare altro che scegliere le due classi che abbiamo creato allo step precedente come Model Class e Data Context Class

XinComicsController

Fatto ciò, dopo aver tritato per qualche secondo magicamente VisualStudio vi genererà il controller con tutti i metodi base funzionanti.

Devo dire che la prima volta che ho generato il controller mi sono beccato uno strano errore “Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly ‘Microsoft.VisualStudio.Web.CodeGeneration.Utils, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60’. The system cannot find the file specified. File name: ‘Microsoft.VisualStudio.Web.CodeGeneration.Utils, Version=5.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60′” che ho risolto con il provvidenziale intervento di StackOverflow [4]. Qualora vi succedesse provateci, a me ha risolto il problema.

Ecco qui dunque il controller bello che pronto:

XinComicsController

In effetti se lanciamo ora il progetto i nuovi metodi sono presenti

XinComics methods

ma se ad esempio proviamo a lanciare la GET ci becchiamo un bel errore:

System.InvalidOperationException: Unable to resolve service for type 'XinCataLogSwaggerWebAPI.Models.XinCataLogContext' while attempting to activate 'XinCataLogSwaggerWebAPI.Controllers.XinComicsController'.

che ci dice in maniera abbastanza esplicita che abbiamo commesso qualche errore nell’inizializzazione del DB context XinCataLogContext. In effetti non lo abbiamo proprio registrato [5].

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<XinCataLogContext>(options =>
              options.UseSqlServer(Configuration.GetConnectionString("DefaultSQLServerConnection")));

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "XinCataLogSwaggerWebAPI", Version = "v1" });
            });
        }

La prima linea di codice del metodo registra il DB context che abbiamo creato prima. Adesso dobbiamo semplicemente referenziare la Connection String nel file appsettings.

  "ConnectionStrings": {
    "DefaultSQLServerConnection": "Server=WIN-7O15VR47QA6;Database=XinCataLog;Trusted_Connection=True;"
  }

E questo è tutto! Se ora lanciate l’applicazione scoprirete che i metodi funzionano come ci attendevamo. Abbiamo ottenuto la prima WebAPI (con Swagger) scrivendo al più un paio di linee di codice visto che la gran parte delle azioni le fa in toto Visual Studio That’s.

Get method from Database

[1] https://github.com/stepperxin/XinCataLogSwaggerWebAPI

[2] https://docs.microsoft.com/en-us/answers/questions/357012/can39t-find-adonet-entity-data-model-missing-visua.html

[3] https://docs.microsoft.com/en-us/aspnet/web-api/overview/older-versions/creating-a-web-api-that-supports-crud-operations

[4] https://stackoverflow.com/questions/45139243/asp-net-core-scaffolding-does-not-work-in-vs-2017

[5] https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro?view=aspnetcore-6.0#register-the-schoolcontext