Understanding the concepts behind asynchronous messaging
Understanding the concepts behind asynchronous messaging
My experiments with Umbraco Unicore Alpha 3 continues, this time with Web API controllers and integrating it with a Blazor WASM app. I am bringing a bit of Carnatic Music into my demo here! I am using Umbraco Unicore as a content store to hold information about ragas (base melodies in Carnatic music) and I am using a Blazor WASM app to display the content.
My content structure looks as shown below. I have a Raga List document type that allows Raga document type as its allowed child node type. The Raga document type holds the information about a raga. The Raga List document type acts like a folder to organise my content better.
And here is my Raga document type.
To create a Web API, I inherit from UmbracoAPIController
. My Web API Controller is called RagaAPIController
. The method GetRagas
is responsible for getting my data. The real change I noticed around Web API controllers in Umbraco Unicore is in the way the data is queried. I am not sure whether this is the right way to do it, but its Umbraco and there is always more than one way to achieve the same thing!
To query the content I use the IPublishedContentQuery
.The IPublishedContentQuery
is an injectable dependency that can be used to query strongly typed content. I inject the IPublishedContentQuery
into my controller and once injected I can use the ContentSingleAtXPath
method passing in the XPath to my content node to get the RagaList node. I can then get the child nodes of this node, loop through them and create a List
of RagaDTO
objects. My API controller looks as shown below.
public class RagaApiController : UmbracoApiController
{
private readonly IPublishedContentQuery _contentQuery;
public RagaApiController(IPublishedContentQuery contentQuery)
{
_contentQuery = contentQuery;
}
public List<RagaDTO> GetRagas()
{
var ragaList = _contentQuery.ContentSingleAtXPath("//home/ragaList");
var ragas = ragaList.Children.OfType<Raga>();
var ragaContent = new List<RagaDTO>();
foreach(var raga in ragas)
{
ragaContent.Add(new RagaDTO {
Name = raga.Name,
RagaType = raga.RagaType,
Notation = raga.Notation
});
}
return ragaContent;
}
}
And my RagaDTO
class is here
public class RagaDTO
{
public string Name { get; set; }
public string RagaType { get; set; }
public string Notation { get; set; }
}
Finally, I need to enable CORS. I have kept it pretty open, allowing all headers, origins and methods in my example by specifying a named policy in the ConfigureServices()
method and enabling the CORS middleware in the Configure()
method in the Startup.cs
. More about CORS in ASP.NET Core can be read here.
services.AddCors(options =>
{
options.AddPolicy("MyBlazorApp", builder =>
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
app.UseCors("MyBlazorApp");
Build and run, and my API is available at umbraco/api/ragaapi/getragas .
I am using a Blazor WASM App to display the information. For this, I first need to create an IRagaService
interface and its implementation that contains a method to reach out to my Web API.
public class RagaService : IRagaService
{
private readonly HttpClient httpClient;
public RagaService(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task<List<RagaDTO>> GetRagas()
{
return await JsonSerializer.DeserializeAsync<List<RagaDTO>>(await httpClient.GetStreamAsync($"umbraco/api/RagaApi/GetRagas"), new JsonSerializerOptions { PropertyNameCaseInsensitive = true});
}
}
public interface IRagaService
{
Task<List<RagaDTO>> GetRagas();
}
This service needs to be registered in the Program.cs
builder.Services.AddHttpClient<IRagaService, RagaService>(s => s.BaseAddress = new Uri("https://localhost:5001/"));
This is the Typed Client Consumption Pattern. More can be read about it here. The JSON returned by the API is deserialized to a List
of RagaDTO
objects. The RagaDTO
class is the same as mentioned before.
Finally, in my Index.Razor
component, I can inject the IRagaService
to reach out to my Web API can display the data.
@page "/"
@inject IRagaService ragaService
@if (ragas != null && ragas.Any())
{
foreach (var raga in ragas.OrderBy(r => r.Name))
{
<div class="row">
<p>@raga.Name - @raga.RagaType</p>
</div>
}
}
@code{
List<RagaDTO> ragas = new List<RagaDTO>();
protected override async Task OnInitializedAsync()
{
ragas = await ragaService.GetRagas();
}
}
And here is a screenshot of what my Blazor App displays :-)
Understanding the concepts behind asynchronous messaging
This article discusses some of the misconceptions about REST, and what is not REST and presents you with the facts.