Blazor CRUD minimal API
Inleiding
In de Blazor CRUD post hebben we beschreven hoe we een ASP .NET Core hosted Blazor Web Assembly App bouwen. Daarbij worden Web API server controllers aangeroepen door een WASM (WebAssembly) client waarbij de Web API server controllers CRUD(Create/Read/Update/Delete)-acties uitvoeren op een gegevensbron. De gegevensbron kan, afhankelijk van de implementatie, een list<T> in het intern geheugen zijn of een tabel in een SQL Server database.
De applicatie gaan we in deze post opnieuw bouwen. De functionaliteit blijft verder helemaal hetzelfde, maar we gaan gebruik maken van een aantal .Net 6 / C# 10-features. Zo worden op de server de CRUD-acties door Minimal APIs gedaan.
De Visual Studio ASP .NET Core hosted-optie gebruiken we in dit voorbeeld niet. We maken in plaats daarvan een aparte minimal API server-solution en een aparte WASM (WebAssembly) client-solution. De WASM-client zal uiteindelijk vanaf de lokale computer van de gebruiker verzoeken doen naar de minimal APIs op een server.
Ten slotte geven we toe aan het nullable-purisme dat in C# 10 de default is. We voegen in de code diverse controles toe op het nullable zijn zodat de compiler ook weer tevreden is en niet meer met waarschuwingen komt en het optreden van Null Reference Exceptions zoveel mogelijk wordt tegengegaan. De voorbeeldcode kun je hier en hier in Github terugvinden.
Server – minimal API
Voor de te bouwen solutions maken we gebruik van Visual Studio 2022. We creëren voor de minimal API server-solution een nieuw project en we gebruiken de ASP .NET Core Web API-project template:
Zie ook deze post voor de te nemen stappen. We stoppen alles in bestand Program.cs zodat in de Solution Explorer het één en ander er als volgt uit zal zien:
Je kunt desgewenst de instelling in het projectbestand aanpassen naar <Nullable>disable</Nullable>. De compiler zal daarmee niet meer met waarschuwingen komen over mogelijke nullable references. In het voorbeeld in deze post houden we de C# 10 default aan m.b.t. de Nullable references:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
...
Modeldefinitie
Een applicatie doet (meestal) iets met gegevens en we maken een modeldefinitie voor datgene waar we wat mee willen doen.
In dit voorbeeld gaat het om een bezitter van een auto. Wat willen we van een autobezitter registreren? We houden het simpel en we registreren van een eigenaar alleen diens naam en de regio waar de autobezitter woont. De modeldefinitie leggen we vast in class definition EIGENAAR.
We definiëren de gegevenstypen als nullable (lege waarden zijn toegestaan en te herkennen aan de ?) zodat de compiler ons niet gaat lastig vallen met allerlei non-nullable waarschuwingen:
public class EIGENAAR
{
public int? ID { get; set; } = 0;
public string? Omschrijving { get; set; } = string.Empty;
public string? Voornaam { get; set; } = string.Empty;
public string? Achternaam { get; set; } = string.Empty;
public string? Regio { get; set; } = string.Empty;
}
IModel
De minimal APIs gebruiken een IModel-interface en we zullen zien dat de IModel-interface een tweetal implementaties gaat krijgen. Ook hier definiëren we het één en anders als nullable (herkenbaar aan de ?) om niet lastig gevallen te worden met waarschuwingen van de compiler over non-nullables. De IModel-interface kent de volgende action methods:
public interface IModel
{
Task<List<EIGENAAR>> HaalOpEigenaren();
Task<EIGENAAR?> HaalopEigenaar(int? ID);
Task<EIGENAAR?> VoegToe(EIGENAAR? eigenaar);
Task<EIGENAAR?> Muteer(EIGENAAR? eigenaar);
Task<bool> Verwijder(int ID);
}
Implementatie
De minimal APIs gebruiken een IModel-interface en de IModel-interface kent een MemoryModel-implementatie waarbij een List<T> in het intern geheugen de datasource is. Daarnaast is er een DBModel-implementatie waarbij een tabel in een SQL Server database de datasource is.
// Interface IModel
// Activeer de implementatie die van toepassing is.
// de MemoryModel-implementatie:
builder.Services.AddSingleton<IModel, MemoryModel>();
// de DBModel-implementatie:
builder.Services.AddScoped<IModel, DBModel>();
We geven in bestand Startup.cs op welke IModel-implementatie van toepassing is. De clients roepen uiteindelijk de minimal APIs aan, maar welke implementatie de Minimal API gebruikt? Dat is iets voor de server en dat stellen we hier in. Het is geen aandachtspunt voor de client.
DBModel
DBModel is een implementatie van de IModel-interface waarbij een EIGENAAR-tabel in een SQL Server database de datasource is. De tabel bestaat uit de volgende velden: ID, omschrijving, regio, achternaam en voornaam
Een dbContext-klasse is nodig voor het ophalen van de gegevens uit de database en voor het bijwerken van de gegevens in die database. De klasse is een intermediair naar de onderliggende database en klasse DbContextClass is een klasse dat erft van klasse DbContext.
builder.Services.AddDbContext
<DbContextClass>
(options => options.UseSqlServer
("name=ConnectionStrings:VOORBEELDConnection"));
....
public class DbContextClass : DbContext
{
public DbContextClass(DbContextOptions
<DbContextClass> options) : base(options) { }
public DbSet<EIGENAAR>
EIGENAAR => Set<EIGENAAR>();
}
In configuratiebestand appsettings.json definiëren we connectiestring VOORBEELDConnection. Er wordt verwezen naar database VOORBEELD met daarin een EIGENAAR-tabel.
{
"Logging": { ... } },
"AllowedHosts": "*",
"ConnectionStrings":
{ "VOORBEELDConnection":
"server=DESKTOP\\SQLEXPRESS;
database=VOORBEELD;integrated security=true;
MultipleActiveResultSets=True;
Trusted_Connection=true"
}
}
DBModel heeft een aantal action methods die aan de gang gaan met een tabel in een database. Zie verder de broncode op GitHub als je wilt zien wat de action methods precies doen en hoe ze per implementatie van elkaar verschillen.
MemoryModel
MemoryModel is een implementatie van de IModel-interface waarbij een List<T> in het intern geheugen de datasource is. De CRUD action methods voeren bewerkingen uit op de gegevens in de List<T>. Toegevoegde en gewijzigde gegevens in de List<T> gaan verloren zodra de applicatie wordt afgesloten. De applicatie komt bij het opstarten elke keer weer met eenzelfde initiële vulling.
MemoryModel heeft dezelfde action methods als DBModel maar de methods doen per implementatie verschillende dingen. Zo gaat de MemoryModel iets doen met een List<T> terwijl de dbModel aan de gang gaat met een tabel in een database. Zie verder de broncode op GitHub als je wilt zien wat de methoden precies doen en hoe ze per implementatie van elkaar verschillen.
Minimal APIs
We definiëren vijf minimal APIs waarbij de minimal API via een endpoint door de client benaderd wordt:
minimal API | EndPoint |
---|---|
HaalOpEigenaren | /HaalOpEigenaren |
HaalopEigenaar(ID) | /HaalOpEigenaar/{ID} |
VoegToe(eigenaar) | /VoegToe |
Muteer(eigenaar) | /Muteer |
Verwijder(ID) | /Verwijder/{ID} |
HaalopEigenaren
De web applicatie start met het tonen van de eigenaren uit de gegevensbron. Afhankelijk van de implementatie kan dat of de inhoud zijn van tabel EIGENAAR of de inhoud van een List<T> in het intern geheugen.
app.MapGet("/HaalOpEigenaren", async (IModel model) =>
{
try
{
return Results.Ok(await model.HaalOpEigenaren());
}
catch (Exception ex)
{
return Results.Problem(
"Er is iets fout gegaan met
minimal API HaalOpEigenaren - " +
ex.Message);
}
});
Minimal API HaalOpEigenaren haalt alleen maar gegevens op uit een gegevensbron waarbij de gegevensbron verder met rust wordt gelaten. Het één en ander komt tot uiting in de MapGet (regel 1) waarmee je aangeeft dat bij de aanroep een GET-verb gebruikt moet worden.
HaalopEigenaar
We hebben voor het ophalen van de gegevens van een eigenaar minimal API HaalOpEigenaar die aan de hand van een ID de gegevens ophaalt van de eigenaar in kwestie.
app.MapGet("HaalOpEigenaar/{ID}",
async (IModel model, int ID) =>
{
try
{
return
Results.Ok(
await model.HaalopEigenaar(ID) is EIGENAAR eigenaar ?
Results.Ok(eigenaar) :
Results.NotFound("Niks gevonden"));
}
catch (Exception ex)
{
return Results.Problem(
"Er is iets fout gegaan met
minimal API HaalOpEigenaar - " +
ex.Message);
}
});
Het betreft verder een GET-request hetgeen tot uiting komt in de MapGet (regel 1).
VoegToe
We hebben voor het toevoegen van een EIGENAAR minimal API VoegToe waarbij een EIGENAAR-object met ID 0 wordt meegegeven en het object wordt gebruikt voor het toevoegen van de gegevens aan de datasource.
app.MapPost("/VoegToe",
async (IModel model, EIGENAAR eigenaar) =>
{
try
{
eigenaar.ID = null;
await model.VoegToe(eigenaar);
return Results.Ok(
await model.HaalopEigenaar(eigenaar.ID));
}
catch (Exception ex)
{
return Results.Problem("Er is iets fout gegaan met
minimal API VoegToe - " +
ex.Message);
}
});
Verder moet voor minimal API VoegToe een POST-request gedaan worden (geen GET-request) wat uiting komt in de MapPost (regel 1).
Muteer
We hebben voor het muteren van de gegevens van een EIGENAAR minimal API Muteer waarbij een EIGENAAR-object wordt meegegeven en het object wordt gebruikt voor het bijwerken van de gegevens in de datasource.
app.MapPut("/Muteer",
async (IModel model, EIGENAAR eigenaar) =>
{
try
{
return Results.Ok(await model.Muteer(eigenaar));
}
catch (Exception ex)
{
return Results.Problem("Er is iets fout gegaan met
minimal API Muteer - " +
ex.Message);
}
});
Verder moet voor minimal API Muteer een PUT-request gedaan worden (geen GET-request) hetgeen tot uiting komt in de MapPut (regel 1).
Verwijder
We hebben voor het verwijderen van de gegevens van een EIGENAAR minimal API Verwijder en het verwijderen gaat wat simpeler. We geven een ID mee en die ID wordt gebruikt voor het verwijderen van de gegevens in de datasource.
De minimal APIs geven via de OK-helper method een waarde terug als alles goed is gegaan. Minimal API Verwijder retourneert via de OK-helper method de waarde true na het succesvol verwijderen van het één en ander.
app.MapDelete("Verwijder/{ID}",
async (IModel model, int ID) =>
{
try
{
return Results.Ok(await model.Verwijder(ID));
}
catch (Exception ex)
{
return Results.Problem("Er is iets fout gegaan met
minimal API Verwijder - " +
ex.Message);
}
});
Verder moet voor minimal API Verwijder een DELETE-request gedaan worden (geen GET-request) hetgeen tot uiting komt in de MapDelete (regel 1).
Resultaat
Voor de minimal API server-solution project hebben we gebruik gemaakt van de ASP .NET Core Web API-project template:
We hebben ook gebruik gemaakt van de Enable OpenAPI support-optie waardoor we out-of-the-box Swagger kunnen gebruiken voor het doen testen van onze minimal APIs:
CORS
De Minimal APIs worden uiteindelijk door een client aangeroepen. De client in het voorbeeld in deze post is een WASM (WebAssembly)-client, maar clients van een ander typ kunnen ook gebruik maken van de diensten van de minimal APIs. Je zou daarbij kunnen denken aan clients die zijn gebouwd in bijvoorbeeld Angular.
We hebben een op zichzelf staande minimal API server-solution gebouwd en verzoeken van clients (WASM, Angular or whatever) zullen afkomstig zijn uit een ander domein dan die waarin de minimal API server-solution zich bevindt.
CORS (Cross Origin Resource Sharing) staat default niet toe dat Web APIs / minimal APIs verzoeken in behandeling nemen van clients die uit andere domeinen afkomstig zijn. We moeten CORS-voorzieningen treffen willen we de server-solution zover krijgen dat die verzoeken van clients uit andere domeinen gaat uitvoeren en de client niet meer met dit wordt geconfronteerd:
We illustreren het één en ander aan de hand van een lokale ontwikkelomgeving welke valt in dit domein: http://localhost:5168/
. We vinden het domein bij de launch-settings:
En we stellen in Program.cs het één en ander als volgt in, waarbij de CORS uiteraard wat strikter ingesteld kan worden, maar we doen het even zo:
// Definieer je CORS
// zie launchSettings.json
// profiel WebAPI - http://localhost:5168/
builder.Services.AddCors
(
options => options.AddPolicy(name: "CORSBeleid",
policy =>
{ policy.WithOrigins("http://localhost:5168")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin();
}));
....
// Activeer CORS
app.UseCors("CORSBeleid");
En de clients zullen met bovenstaande instelling nu wel toegang hebben tot de Web APIs / minimal APIs en de daarbij horende datasources.
Client – WASM
We gaan de minimal APIs vanuit een WebAssembly (WASM)-client aanroepen. Voor de te bouwen WASM-client-solution maken we gebruik van Visual Studio 2022. We creëren voor de WASM-client-solution een nieuw project en we gebruiken de Blazor WebAssembly App-project template:
De Visual Studio ASP .NET Core hosted-optie gebruiken we in dit voorbeeld niet. We bouwen een op zichzelf staande WASM-client-solution. De WASM-client zal een ander domein hebben dan die waarin de minimal API server-solution zich bevindt, maar dat zou geen problemen meer moeten geven. We hebben immers al wat CORS-voorzieningen getroffen bij de minimal API server-solution.
Configuratie
De minimal API server-solution heeft deze url: http://localhost:5168/
. De url gaan we niet “hard” opnemen in de code van de client en we besluiten tot het laten doen opnemen van de url in configuratiebestand appsettings.json. We kijken dan in het configuratiebestand als we de url van de minimal api server solution willen weten.
Bij een client-solution wordt alles gekopieerd naar de lokale computer van de gebruiker waaronder het configuratiebestand. Sla geen gevoelige gegevens op in het configuratiebestand van de client omdat de inhoud van dat configuratiebestand voor iedereen zichtbaar zal zijn.
Een url beschouwen we niet als zijnde gevoelige informatie en de server biedt genoeg mogelijkheden om ongeoorloofde toegang tot en onbevoegd gebruik van web APIs / minimal APIs tegen te gaan. We zien het niet als een risico als we voor het vastleggen van een url gebruik gaan maken van een configuratiebestand.
De Blazor WebAssembly App-project-template voorziet default niet in de aanmaak en het gebruik van een appsettings.json-configuratiebestand. Neem, indien nog niet aanwezig deze regel op in program.cs om gebruik te kunnen maken van een appsettings.json-configuratiebestand:
builder.Services.AddScoped
(sp => new HttpClient
{
BaseAddress =
new Uri(builder.HostEnvironment.BaseAddress)
});
Vergeet deze using-statement niet in bestand _Imports.razor:
@using Microsoft.Extensions.Configuration
En maak ten slotte bestand appsettings.json aan onder de wwwroot:
Voorzie het configuratiebestand van inhoud. We slaan alles op als key-value pairs waarbij we een key hebben met de waarde “apiLocatie” en een bijbehorende value zijnde de url die we willen registreren:
{
"apiLocatie": "http://localhost:5168"
}
Lees appsettings.json als volgt uit in je Razor-componenten waarbij we gebruik maken van de apiLocatie-key voor het doen ophalen van de url:
// Maak gebruik van deze interface
@inject IConfiguration Configuration
...
@code {
...
// Haal de value op aan de hand van de apiLocatie-key:
... = await httpClient.GetAsync(
Configuration["apiLocatie"] + "/HaalOpEigenaren");
...
}
Razor componenten
In de client project creëren we een aantal Razor-componenten en in die Razor-componenten gebruiken we de @page-directive zodat de Razor-component als een pagina is op te starten. Vanuit elke Razor-component roepen we een minimal API aan. De minimal API geeft iets terug en het geretourneerde wordt uiteindelijk in de Razor component (pagina) getoond:
Eigenaren
Eigenaren.razor is het startpunt van de applicatie. Eigenaren.razor toont alle eigenaren uit de datasource en dat kan, afhankelijk van de implementatie, de inhoud van tabel EIGENAAR zijn of een List<T> in het intern geheugen:
In Eigenaren.razor injecteren we een IConfiguration zodat we de appsettings.json kunnen uitlezen en een HttpClient zodat we de minimal APIs op de server kunnen aanroepen. Verder injecteren we een NavigationManager zodat we naar andere pagina’s kunnen navigeren. Alle eigenaren uit de datasource worden uiteindelijk in een <table> in de pagina getoond.
In OnInitializedAsync wordt de code getriggered die moet gaan draaien zodra de pagina wordt geladen. We zien dat minimal API HaalOpEigenaren wordt aangeroepen. We zien ook dat met Configuration[“apiLocatie”] configuratiebestand appsettings.json wordt uitgelezen wat uiteindelijk zal moeten leiden tot de aanroep van de minimal API met endpoint: http://localhost:5168/HaalopEigenaren
/
Het resultaat van de aanroep van de minimal API is een array met JSON-objecten en die zettten we in private variabele eigenaren. We zien dat in de code de variabelen nullable (herkenbaar aan de “?“) zijn gemaakt en dat de nodige controles aanwezig zijn om te controleren of de inhoud van een variabele leeg (null) is of niet.
Private variabele eigenaren zou gevuld moeten zijn indien minimal API HaalOpEigenaren naar behoren heeft gedraaid en we kunnen door de array “itereren” en de <table> vullen:
Ten slotte de code die getriggerd wordt als op bepaalde buttons wordt geklikt. We zien dat we doorgestuurd worden naar respectievelijk EigenaarVoegToe.razor, EigenaarMuteer.razor en EigenaarVerwijder.razor. Voor de laatste twee Razor-componenten wordt een ID meegegeven zodat de gegevens van een eigenaar gevonden kunnen worden in de datasource.
EigenaarVoegToe
We klikken op de Voeg Toe-button in Eigenaren.razor en we komen bij EigenaarVoegToe.razor.
We gebruiken in de Razor-component een <EditForm> waarin de gegevens van de toe te voegen eigenaar worden gezet. Het formulier is “gebind” aan private object eigenaar.
We worden teruggestuurd naar Eigenaren.razor als we klikken op de Terug-button:
private void Terug()
{
navigationManager.NavigateTo($"/eigenaren");
}
Het onderstaande wordt getriggerd bij het klikken op de Opslaan-button. We maken voor het toevoegen van de gegevens gebruik van minimal API VoegToe. Private object eigenaar wordt meegegeven bij de aanroep van de minimal API die deze endpoint heeft: http://localhost:5168/VoegToe
HaalOpEigenaar
Het is, als we iets willen wijzigen, handig dat we eerst zien wat we willen wijzigen en dat geldt ook voor het verwijderen. We hebben voor het ophalen van de gegevens van een eigenaar minimal API HaalOpEigenaar waarbij een eigenaar-ID wordt meegegeven bij het doen ophalen van de gegevens.
Zo halen we met Configuration[“apiLocatie”] uit configuratiebestand appsettings.json de url op en uiteindelijk zal minimal API HaalOpEigenaar aangeroepen worden waarbij de minimal API deze endpoint heeft: http://localhost:5168/HaalopEigenaar/1
en de “1” voor ID “1” staat:
var opgehaald =
await httpClient
.GetAsync(Configuration["apiLocatie"] +
"/HaalOpEigenaar" + "/" + ID);
Het resultaat van de aanroep van de minimal API is een opgehaald-object met onderstaande klassendefinitie. Alle klassendefinities staan in de WebAssembly (WASM)-client project in de Shared-folder.
public class Inhoud
{
public class Value
{
public int? id { get; set; } = 0;
public string? omschrijving { get; set; } = string.Empty;
public string? voornaam { get; set; } = string.Empty;
public string? achternaam { get; set; } = string.Empty;
public string? regio { get; set; } = string.Empty;
}
public Value? value { get; set; }
public int StatusCode { get; set; }
public string? ContentType { get; set; } = string.Empty;
}
We lezen object opgehaald uit en we vinden uiteindelijk de gegevens van de eigenaar in het object. We zetten de gegevens in private object eigenaar .
private EIGENAAR eigenaar = new EIGENAAR();
...
var opgehaald =
await httpClient
.GetAsync(Configuration["apiLocatie"] +
"/HaalOpEigenaar" + "/" + ID);
...
if (!opgehaald.IsSuccessStatusCode)
{
fout = await
opgehaald.Content.ReadFromJsonAsync<Fout>();
if (fout is not null)
{
if (fout.Detail is not null)
{
resultaat = "De eigenaar met ID " +
ID + " kan niet gevonden worden. " +
fout.Detail.ToString() +
" (" + opgehaald.StatusCode + ")";
}
}
}
else
{
inhoud = await
opgehaald.Content
.ReadFromJsonAsync<Inhoud>();
if (inhoud is not null && inhoud.value is not null)
{
eigenaar.ID = inhoud.value.id;
eigenaar.Achternaam = inhoud.value.achternaam;
eigenaar.Voornaam = inhoud.value.voornaam;
eigenaar.Regio = inhoud.value.regio;
eigenaar.Omschrijving = inhoud.value.omschrijving;
...
};
}
We gebruiken in de Razor-componenten een <EditForm> waarin de gegevens van de eigenaar worden gezet. Het formulier is “gebind” aan private object eigenaar.
EigenaarMuteer
We klikken op de Wijzig-button in Eigenaren.razor en we komen voor een bepaalde eigenaar uit bij EigenaarMuteer.razor.
In de OnInitializedAsync wordt de code getriggered die moet gaan draaien zodra de pagina wordt geladen en minimal API HaalOpEigenaar wordt daarbij aangeroepen. De minimal API vult een eigenaar private object en het object bevat de gegevens die gewijzigd gaan worden.
We gebruiken in de Razor-componenten een <EditForm> waarin de gegevens van de eigenaar worden gezet. Het formulier is “gebind” aan private object eigenaar:
We worden teruggestuurd naar Eigenaren.razor als we klikken op de Terug-button:
private void Terug()
{
navigationManager.NavigateTo($"/eigenaren");
}
We maken voor het opslaan van de gewijzigde gegevens gebruik van minimal API Muteer. Object eigenaar wordt meegegeven bij de aanroep van de minimal API waarbij de minimal API deze endpoint heeft: http://localhost:5168/Muteer
EigenaarVerwijder
We klikken op de Verwijder-button in Eigenaren.razor en we komen voor een bepaalde eigenaar uit bij EigenaarVerwijder.razor.
We gebruiken geen <EditForm> in EigenaarVerwijder.razor. We verplichten de gebruiker tot het bekijken van de te verwijderen gegevens en daarna mag de gebruiker overgaan tot de daadwerkelijke verwijdering. Het is verder niet de bedoeling dat in dit scherm gegevens gewijzigd worden.
We maken voor het verwijderen van de eigenaar gebruik van minimal API Verwijder waarbij de minimal api deze endpoint heeft: http://localhost:5168/Verwijder/1
en de “1” voor ID “1” staat. Minimal API Verwijder heeft aan een ID genoeg voor het doen verwijderen van een eigenaar.
We weten de ID omdat bij de OnInitializedAsync minimal API HaalOpEigenaar is aangeroepen die private object eigenaar vult met o.a. de ID van de te verwijderen eigenaar.
Slot
In deze post hebben we een minimal API server-solution gebouwd en een WASM (WebAssembly) client-solution. Bij de uiteindelijke deployment zal de WASM-client vanaf de lokale computer van de gebruiker verzoeken doen naar de minimal APIs op een server.
We hebben bij het bouwen van de minimal API server-solution gebruik gemaakt van de Enable OpenAPI support-optie waardoor we out-of-the-box Swagger kunnen gebruiken voor het doen testen van onze minimal APIs.
In het voorbeeld is de client een WASM-client, maar clients van een ander typ kunnen ook gebruik maken van de diensten van de minimal API server-solution. Je zou daarbij kunnen denken aan clients die zijn gebouwd in bijvoorbeeld Angular.
De minimal API server solution kent twee implementaties. Een implementatie waarbij een List<T> in het intern geheugen de datasource is en een implementatie waarbij een tabel in een SQL Server database de datasource is. We geven binnen de minimal API server-solution op welke implementatie van toepassing is. Het is niet de client die bepaalt welke implementatie van toepassing is.
Minimal APIs (en Web APIs) nemen default geen verzoeken in behandeling van clients uit andere domeinen. De solutions hebben hun eigen domeinen en we moeten wat CORS (Cross Origin Resource Sharing)-voorzieningen treffen willen we de server-solution zover krijgen dat die verzoeken van clients uit andere domeinen gaat uitvoeren.
We hielden bij het bouwen de C# 10 default aan m.b.t. de nullable references en we zagen dat we in de code diverse controles moesten toevoegen op het nullable (leeg) zijn van variabelen opdat het optreden van Null Reference Exceptions zoveel mogelijk wordt tegengegaan.
Dit was weer een lange post, maar in de post hebben we alle aspecten de revue laten doen passeren die van belang zijn voor de bouw van een WASM-client en een minimal API server-solution. Hopelijk ben je met deze posting weer wat wijzer geworden en ik hoop je weer terug te zien in één van mijn volgende blog posts. Wil je weten wat ik nog meer over Blazor heb geschreven? Hit the Blazor button…