.NET 9.0, Blazor, ASP.NET Core Web API, Azure, Auth0, FluentUI, FluentValidation, Backend for Frontend (BFF), Entity Framework Core, MS SQL Server, SQLite
A .NET 9.0 Blazor framework for hosting and building Blazor applications using the Backend for Frontend (BFF) pattern. It comes with authentication, authorisation, change tracking, and persisting structured logs to the database. Follow the instructions for publishing to Azure.
See the Worked Examples for step-by-step guidance on how to introduce new modules into the Atlas framework.
- Setup the Solution
- Authentication
- Authorization
- Support Role
- Navigation
- Audit
- Publish Atlas to Azure
- Worked Examples
- Notes
In the Solution Properties, specify multiple startup projects and set the action for both Atlas.API Web API and Atlas.Blazor.Web.App Blazor application, to Start.
In the Atlas.API appsettings.json set the connection strings, configure Auth0 settings and generating seed data.
Note
Read the next section on Authentication for how to configure Auth0 as the identity provider.
{
"ConnectionStrings": {
"DefaultConnection": "" 👈 set the Atlas database connection string
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Error",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "", 👈set the Atlas database connection string for Serilogs MS SqlServer
"tableName": "Logs",
"autoCreateSqlTable": true,
"columnOptionsSection": {
"customColumns": [
{
"ColumnName": "User",
"DataType": "nvarchar",
"DataLength": 450
},
{
"ColumnName": "Context",
"DataType": "nvarchar",
"DataLength": 450
}
]
}
}
}
]
},
"AllowedHosts": "*",
"Auth0": {
"Domain": "", 👈specify the Auth0 domain
"Audience": "https://Atlas.API.com" 👈specify the audience
},
"SeedData": {
"GenerateSeedData": "true", 👈 set to true to create seed data including modules, categories, pages, users, permissions and roles.
"GenerateSeedLogs": "true" 👈 set to true to generate mock logs
}
}In the Atlas.Blazor.Web.App appsettings.json configure Auth0 settings and specify the Atlas.API url.
Note
Read the next section on Authentication for how to configure Auth0 as the identity provider.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Error",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "", 👈set the Atlas database connection string for Serilogs MS SqlServer
"tableName": "Logs",
"autoCreateSqlTable": true,
"columnOptionsSection": {
"customColumns": [
{
"ColumnName": "User",
"DataType": "nvarchar",
"DataLength": 450
},
{
"ColumnName": "Context",
"DataType": "nvarchar",
"DataLength": 450
}
]
}
}
}
]
},
"AllowedHosts": "*",
"Auth0": {
"Domain": "", 👈specify the Auth0 domain
"ClientId": "", 👈specify the Auth0 ClientId
"ClientSecret": "", 👈specify the Auth0 ClientSecret
"Audience": "https://Atlas.API.com" 👈specify the audience
},
"AtlasAPI": "https://localhost:44420" 👈specify the AtlasAPI url
}Use the .NET CLI for Entity Framework to create your database and create your schema from the migration. In the Developer Powershell or similar, navigate to the Atlas.API folder and run the following command.
dotnet ef database update --project ..\..\data\Atlas.Migrations.SQLServer
In the Atlas.API appsettings.json configuration file set GenerateSeedData and GenerateSeedLogs to true. This will populate the database with seed data at startup.
Warning
If "GenerateSeedData": "true" the tables in the Atlas database will be truncated and repopulated with seed data every time the application starts. Existing data will be permanently lost.
"SeedData": {
"GenerateSeedData": "true", 👈 set to true to create seed data including modules, categories, pages, users, permissions and roles.
"GenerateSeedLogs": "true" 👈 set to true to generate mock logs
}Atlas is setup to use Auth0 as its authentication provider, although this can be swapped out for any provider supporting OAuth 2.0. With Auth0 you can create a free account and it has a easy to use dashboard for registering applications, and creating and managing roles and users.
Using the Auth0, register the Atlas.API Web API and Atlas.Blazor.Web.App Blazor application, and create a atlas-user role and users.
In the Auth0 dashboard create a role called atlas-user. This role must be assigned to all users wishing to access the Atlas application.
Important
Atlas users must be assigned the atlas-user role in Auth0 to access the Atlas application.
Create Auth0 users. The user's Auth0 email claim is mapped to the email of an authorised user in the Atlas database.
Important
Atlas users must be assigned the atlas-user role to access the Atlas application.
Tip
SeedData.cs already contains some pre-defined sample users with roles and permissions. Either create these users in Auth0, or amend the sample users in SeedData.cs to reflect those created in Auth0.
Warning
If "GenerateSeedData": "true" the tables in the Atlas database will be truncated and repopulated with seed data every time the application starts. Existing data will be permanently lost.
private static void CreateUsers()
{
if (dbContext == null) throw new NullReferenceException(nameof(dbContext));
users.Add("alice", new User { Name = "alice", Email = "[email protected]" });
users.Add("jane", new User { Name = "jane", Email = "[email protected]" });
users.Add("bob", new User { Name = "bob", Email = "[email protected]" });
users.Add("grant", new User { Name = "grant", Email = "[email protected]" });
foreach (User user in users.Values)
{
dbContext.Users.Add(user);
}
dbContext.SaveChanges();
}The following article explains how to register and secure a minimal WebAPI with Auth0 with the relevant parts in the Atlas.API Program.cs.
//....existing code removed for brevity
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = $"https://{builder.Configuration["Auth0:Domain"]}";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = builder.Configuration["Auth0:Domain"],
ValidAudience = builder.Configuration["Auth0:Audience"]
};
});
builder.Services.AddAuthorizationBuilder()
.AddPolicy(Auth.ATLAS_USER_CLAIM, policy =>
{
policy.RequireAuthenticatedUser().RequireRole(Auth.ATLAS_USER_CLAIM);
});
//....existing code removed for brevity
app.UseAuthentication();
app.UseAuthorization();
//....existing code removed for brevityWhen mapping the minimal Web API methods add RequireAuthorization(Auth.ATLAS_USER_CLAIM), as can be seen here in AtlasEndpointMapper.cs.
//....existing code removed for brevity
app.MapGet($"/{AtlasAPIEndpoints.GET_CLAIM_MODULES}", ClaimEndpoint.GetClaimModules)
.WithOpenApi()
.WithName(AtlasAPIEndpoints.GET_CLAIM_MODULES)
.WithDescription("Gets the user's authorized modules")
.Produces<IEnumerable<Module>?>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status500InternalServerError)
.RequireAuthorization(Auth.ATLAS_USER_CLAIM); // 👈 add RequireAuthorization to endpoints
//....existing code removed for brevityThe following article explains how to register and add Auth0 Authentication to Blazor Web Apps in .NET 8.0.
Warning
.NET 9.0 simplifies this approach by providing services to serialize the authentication state on the server and deserialize the authentication state in the WebAssembly client.
See this article for details explaining Authentication State Serialization for Blazor Web Apps.
See Microsoft's documentation to Manage authentication state in Blazor Web Apps.
Here are the relevant parts in the Atlas.Blazor.Web.App Program.cs.
//....existing code removed for brevity
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents()
.AddAuthenticationStateSerialization(); // 👈 adds the services to serialize the authentication state on the server
//....existing code removed for brevity
builder.Services
.AddAuth0WebAppAuthentication(Auth0Constants.AuthenticationScheme, options =>
{
options.Domain = builder.Configuration["Auth0:Domain"] ?? throw new NullReferenceException("Auth0:Domain");
options.ClientId = builder.Configuration["Auth0:ClientId"] ?? throw new NullReferenceException("Auth0:ClientId");
options.ClientSecret = builder.Configuration["Auth0:ClientSecret"] ?? throw new NullReferenceException("Auth0:ClientSecret");
options.ResponseType = "code";
}).WithAccessToken(options =>
{
options.Audience = builder.Configuration["Auth0:Audience"] ?? throw new NullReferenceException("Auth0:Audience");
});
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<TokenHandler>();
//....existing code removed for brevity
app.MapGet("login", async (HttpContext httpContext, string redirectUri = @"/") =>
{
AuthenticationProperties authenticationProperties = new LoginAuthenticationPropertiesBuilder()
.WithRedirectUri(redirectUri)
.Build();
await httpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
});
app.MapGet("logout", async (HttpContext httpContext, string redirectUri = @"/") =>
{
AuthenticationProperties authenticationProperties = new LogoutAuthenticationPropertiesBuilder()
.WithRedirectUri(redirectUri)
.Build();
await httpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
});
//....existing code removed for brevityHere is the relevant part in the Atlas.Blazor.Web.Client Program.cs.
WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddFluentUIComponents();
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthenticationStateDeserialization(); // 👈 adds the services to deserialize the authentication state in the WebAssembly client
await builder.Build().RunAsync();Finally, the following article describes how to call protected APIs from a Blazor Web App, including calling external APIs which requires injecting the access token into HTTP requests. This is handled by the TokenHandler.cs.
public class TokenHandler(IHttpContextAccessor httpContextAccessor) : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if(_httpContextAccessor.HttpContext == null) throw new NullReferenceException(nameof(_httpContextAccessor.HttpContext));
string? accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token").ConfigureAwait(false);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await base.SendAsync(request, cancellationToken);
}
}Clicking the Login button on the top right corner of the application will re-direct the user to the Auth0 login page. Once authenticated, the user is directed back to the application, and the navigation panel will display the Modules, Categories and Pages the user has permission to access.
Click the Login button on the top right corner to be redirected to the Auth0 login page.
Authenticate in Auth0.
The Auth0 callback redirects the authenticated user back to Atlas, and the navigation panel will display the Modules, Categories and Pages the user has permission to access.
Atlas users are maintained in the Atlas database. The user's email in the Atlas database corresponds to the email claim provided by Auth0 to authenticated users. When a user is authenticated, a lookup is done in the Atlas database to get the users roles and permissions. This will determine which modules, categories and pages the user has access to in the navigation panel. It will also provide more granular permissions in each rendered page e.g. read / write.
Creating, updating and deleting Atlas users, roles and permissions, is done in the Authorisation category of the Administration module.
Tip
The Authorisation category of the Administration module is only accessible to users who are members of the Admin-Read Role and Admin-Write Role.
The Admin-Read Role gives read-only view of users, roles and permissions, while the Admin-Write Role permit creating, updating and deleting them.
Here we see user Bob is assignd the roles Support Role and User Role.
Here we see the role Support Role, the list of permissions it has been granted, and we can see Bob is a member of the role.
Here we see the permission Support, and the roles that have been granted the Support permission.
Modules are applications, and can be related or unrelated to each other. Each module consists of one or more categories. Each category groups related pages. A page is a routable razor @page.
Creating, updating and deleting modules, categories and pages, is done in the Applications category of the Administration module.
Tip
Because each page must point to a routable razor @page, the Applications category of the Administration module is only accessible to users who are members of the Developer Role.
i.e. creating, updating and deleting modules, categories and pages is a developer concern.
Each module, category and page in the Navigation panel has a permission, and are only accessible to users who have been assigned that permission via role membership.
Here we see the Support module, the order it appears in the navigation panel, the permission required for it to appear in the navigation panel, and the icon that is displayed with it in the navigation panel. We see it has an Events category. We can also see highlighted in yellow how it appears in the navigation panel.
Here we see the Events category, the module it belongs to, the order it appears under the module in the navigation panel, the permission required for it to appear in the navigation panel, and the icon that is displayed with it in the navigation panel. We also see it has a page called Logs.
Here we see the Logs page, the category it belongs to, the order it appears under the category in the navigation panel, the permission required for it to appear in the navigation panel, and the icon that is displayed with it in the navigation panel. Crucially, we also see the route, which is the routable razor @page that it navigates to when the user clicks the page in the navigation panel.
Here we can see the Logs.razor component, with its routable @page attribute.
Important
The route specified in the page must map to a valid @page attribute on a routable component.
@page "/Logs"
@using System.Text.Json
@rendermode @(new InteractiveServerRenderMode(prerender: false))
@attribute [StreamRendering]
<PageTitle>Logs</PageTitle>
@if (_alert == null)
{
<FluentCard>
<FluentHeader>
Logs
</FluentHeader>
<!-- code removed for brevity -->
Tip
The Support module, its categories and routable pages, are only accessible to users who are members of the Support Role.
Note
Members of the Support Role also have Admin-Read and Admin-Write permissions, permitting them to add, update and delete users.
Logs are persisted to the Logs table in the Atlas database and are viewable to members of the Support Role.
Here we can see mock logs created at startup when "GenerateSeedLogs": "true" is set in the Atlas.API's appsettings.json.
Clicking on the log entry will display the full log details in a popup box.
The ApplicationDbContext.cs uses EF Change Tracking to capture OldValue and NewValues from INSERT's, UPDATE's and DELETE's, for entities where their poco model class inherits from ModelBase.cs. Tracked changes can be queried in the Audit table of the Atlas database.
More can be read here about change tracking in Entity Framework:
- Change Tracking in EF Core
- Change Detection and Notifications
- Tracking Changes of Entities in EF Core
First, create an Azure account.
Create a Resource Group called atlas-rg and select an appropriate Region.
In the Resource Group, create a Web App + Database called atlas-web-api.
- Select the same Region as the Resource Group.
- Select the Runtime stack.
- Stick with the
SQLAzureEngine, and default server and database names provided. - For the Hosting plan, select
Basic - For hobby or research purposes.
In the Resource Group, create a Web App called atlas-blazor.
- Select the Runtime stack.
- For Operating system select
Linux. - Select the same Region as the Resource Group.
- Skip Database, in the Deployment section ensure Basic authentication is selected, and keep the rest of the default settings.
Go to atlas-web-api resource.
For this demo we will use the config in the appsettings.Production.json file for the Atlas.API and Atlas.Blazor.Web.App projects.
- In Environment variables -> Connection strings, copy the connection string from the environment variable
AZURE_SQL_CONNECTIONSTRING, and paste it intoAtlasConnectionin theappsettings.Production.jsonfile for both projects. - Delete environment variable
AZURE_SQL_CONNECTIONSTRING. - Click Apply -> Confirm
Login to Auth0 and:
- add the
atlas-blazorurl toAllowed Callback URLse.g.{atlas-blazor url here...}/callback - add the
atlas-blazorurl toAllowed Logout URLse.g.{atlas-blazor url here...}/
In the Atlas.API project's appsettings.Production.json update the following:
{
"ConnectionStrings": {
"AtlasConnection": "" // 👈 connection string to atlas-web-api-database
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"AllowedHosts": "*",
"Auth0": {
"Domain": "", // 👈 Auth0 domain
"Audience": "https://Atlas.API.com"
},
"CorsOrigins": {
"Policy": "atlas-cors-policy",
"Urls": "" // 👈 url to atlas-blazor app
},
"Database": {
"MigrateDatabase": "true",
"CreateDatabase": "true",
"GenerateSeedData": "true",
"GenerateSeedLogs": "false"
}
}- Right click the project and select Publish.
- For Target select
Azure. - For Specific target select
Azure App Service (Linux). - For App Service select
atlas-web-api. - For API Management tick
Skip this step. - For Deployment type select
Publish (generates pubxml file). - In the projects Properties -> PublishProfiles folder, add the following to
atlas-web-api - Zip Deploy.pubxml.
<ItemGroup>
<Content Update="appsettings.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>In the Publish tab click the publish button. This will build and publish Atlas.API to atlas-web-api. A new tab will open in your default browser for the running atlas-web-api. Append /swagger/index.html to the url and hit enter.
- In the Atlas.Blazor.Web.App project's
appsettings.Production.jsonupdate the following:
{
"ConnectionStrings": {
"AtlasConnection": "" // 👈 connection string to atlas-web-api-database
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"AllowedHosts": "*",
"Auth0": {
"Domain": "", // 👈 Auth0 domain
"ClientId": "", // 👈 Auth0 ClientId
"ClientSecret": "", // 👈 Auth0 ClientSecret
"Audience": "https://Atlas.API.com"
},
"AtlasAPI": "" // 👈 url to atlas-web-api
}- Right click the project and select Publish.
- For Target select
Azure. - For Specific target select
Azure App Service (Linux). - For App Service select
atlas-blazor. - For Deployment type select
Publish (generates pubxml file). - In the projects Properties -> PublishProfiles folder, add the following to
atlas-blazor - Zip Deploy.pubxml.
<ItemGroup>
<Content Update="appsettings.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>In the Publish tab click the publish button. This will build and publish Atlas.Blazor.Web.App to atlas-blazor. A new tab will open in your default browser for the running atlas-blazor.
Click login to be re-directed to Auth0 for authentication.
Auth0 will re-direct bck to the callback page.
Create a Blazor Template module for the standard template WeatherForecast and Counter pages.
- Add a new permission constant in Atlas.Core's
Authclass.
public static class Auth
{
public const string ATLAS_USER_CLAIM = "atlas-user";
public const string ADMIN_READ = "Admin-Read";
public const string ADMIN_WRITE = "Admin-Write";
public const string DEVELOPER = "Developer";
public const string SUPPORT = "Support";
public const string BLAZOR_TEMPLATE = "Blazor-Template"; // 👈 new Blazor-Template permission
}- Create a new
WeatherForecastclass in Atlas.Core'sModelsfolder.
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}- Create a new
WeatherEndpointsclass in Atlas.API'sEndpointsfolder.
public class WeatherEndpoints
{
internal static async Task<IResult> GetWeatherForecast(IClaimData claimData, IClaimService claimService, ILogService logService, CancellationToken cancellationToken)
{
Authorisation? authorisation = null;
try
{
authorisation = await claimData.GetAuthorisationAsync(claimService.GetClaim(), cancellationToken)
.ConfigureAwait(false);
if (authorisation == null
|| !authorisation.HasPermission(Auth.BLAZOR_TEMPLATE))
{
return Results.Unauthorized();
}
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
IEnumerable<WeatherForecast> forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
});
return Results.Ok(forecasts);
}
catch (AtlasException ex)
{
logService.Log(Core.Logging.Enums.LogLevel.Error, ex.Message, ex, authorisation?.User);
return Results.StatusCode(StatusCodes.Status500InternalServerError);
}
}
}- Add a new constant for the weather forecast endpoint in Atlas.Core's
AtlasAPIEndpointsclass.
public static class AtlasAPIEndpoints
{
// existing code removed for brevity
public const string GET_WEATHER_FORECAST = "getweatherforecast"; // 👈 new getweatherforecast endpoint constant
}- Map the weather forecast endpoint in Atlas.API's
ModulesEndpointMapperclass.
internal static class ModulesEndpointMapper
{
internal static WebApplication? MapAtlasModulesEndpoints(this WebApplication app)
{
// Additional module API's mapped here...
app.MapGet($"/{AtlasAPIEndpoints.GET_WEATHER_FORECAST}", WeatherEndpoints.GetWeatherForecast)
.WithOpenApi()
.WithName(AtlasAPIEndpoints.GET_WEATHER_FORECAST)
.WithDescription("Gets the weather forecast")
.Produces<IEnumerable<WeatherForecast>?>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status500InternalServerError)
.RequireAuthorization(Auth.ATLAS_USER_CLAIM);
return app;
}
}- Create a new interface
IWeatherForecastRequestsin Atlas.Requests'sInterfacesfolder.
public interface IWeatherForecastRequests
{
Task<IEnumerable<WeatherForecast>?> GetWeatherForecastAsync();
}- Create a new class
WeatherForecastRequestsin Atlas.Requests'sAPIfolder.
public class WeatherForecastRequests(HttpClient httpClient) : IWeatherForecastRequests
{
protected readonly HttpClient _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
protected readonly JsonSerializerOptions _jsonSerializerOptions = new(JsonSerializerDefaults.Web);
public async Task<IEnumerable<WeatherForecast>?> GetWeatherForecastAsync()
{
return await JsonSerializer.DeserializeAsync<IEnumerable<WeatherForecast>?>
(await _httpClient.GetStreamAsync(AtlasAPIEndpoints.GET_WEATHER_FORECAST)
.ConfigureAwait(false), _jsonSerializerOptions).ConfigureAwait(false);
}
}- Register the
WeatherForecastRequestsservice in Atlas.Blazor.Web.App'sProgramfile.
// existing code removed for brevity
builder.Services.AddTransient<IWeatherForecastRequests, WeatherForecastRequests>(sp =>
{
IHttpClientFactory httpClientFactory = sp.GetRequiredService<IHttpClientFactory>();
HttpClient httpClient = httpClientFactory.CreateClient(AtlasWebConstants.ATLAS_API);
return new WeatherForecastRequests(httpClient);
});
WebApplication app = builder.Build();
// existing code removed for brevity- Create a new Blazor
Weathercomponent in Atlas.Blazor.Web's/Components/Pagesfolder
@page "/Weather"
@attribute [StreamRendering]
@rendermode @(new InteractiveServerRenderMode(prerender: false))
<PageTitle>Weather</PageTitle>
<FluentLabel Typo="Typography.PageTitle">Weather</FluentLabel>
<br>
<FluentLabel Typo="Typography.PaneHeader">This component demonstrates showing data.</FluentLabel>
<br>
@if (Forecasts == null)
{
<FluentLabel Typo="Typography.Subject">Loading...</FluentLabel>
}
else
{
<FluentDataGrid TGridItem=Atlas.Core.Models.WeatherForecast Items="@Forecasts"
Style="height: 600px;overflow:auto;" GridTemplateColumns="0.25fr 0.25fr 0.25fr 0.25fr"
ResizableColumns=true GenerateHeader="GenerateHeaderOption.Sticky">
<PropertyColumn Property="@(f => f.Date.ToShortDateString())" Title="Date" Sortable="true" />
<PropertyColumn Property="@(f => f.TemperatureC)" Sortable="true" />
<PropertyColumn Property="@(f => f.TemperatureF)" Sortable="true" />
<PropertyColumn Property="@(f => f.Summary)" Sortable="true" />
</FluentDataGrid>
}
@code {
[Inject]
public IWeatherForecastRequests? WeatherForecastRequests { get; set; }
private IEnumerable<WeatherForecast>? _forecasts;
public IQueryable<WeatherForecast>? Forecasts
{
get
{
return _forecasts?.AsQueryable();
}
}
protected override async Task OnInitializedAsync()
{
if (WeatherForecastRequests == null) throw new NullReferenceException(nameof(WeatherForecastRequests));
_forecasts = await WeatherForecastRequests.GetWeatherForecastAsync().ConfigureAwait(false);
}
}- Create a new Blazor
Countercomponent in Atlas.Blazor.Web's/Components/Pagesfolder.
@page "/Counter"
@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
<FluentLabel Typo="Typography.PageTitle">Counter</FluentLabel>
<br>
<FluentLabel Typo="Typography.PaneHeader">Current count: @currentCount</FluentLabel>
<br>
<FluentButton Appearance="Appearance.Accent" OnClick="@IncrementCount">Click me</FluentButton>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}-
Create a new user
[email protected]in Auth0 and assign the user theatlas-userrole. -
Create the role
Blazor Template Roleand assign it theBlazor-Templatepermission. -
Create the user
[email protected]and assign theBlazor Template Rolerole. -
Create the category
Templatesand set the Module toBlazor Template. -
Log out, then log back in as user
[email protected] -
In the navigation panel, user
[email protected]only has permission to the Blazor Template module.
What the Fluent UI quick guide doesn't tell you is you must also add a reference to /_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css.
For the Blazor Web App project, add the reference to the top of the app.css file in wwwroot:
@import '/_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';For the Blazor WebAssembly stand alone project, add the reference to the index.html file in wwwroot.
<Link href="_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css" rel="stylesheet" />