- Ensuring accessibility
- Razor
- Rest API
- .NET
- App model
- Package
- Web Development with Blazor
- ASP.NET
- Tips
As Tim Berners-Lee, the creator of the World Wide Web, once said: "The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect."
- Write your first C# code
- Get started with web development using Visual Studio Code
- Learn the basics of web accessibility
- Create a web UI with ASP.NET Core
- Create a web API with ASP.NET Core controllers
- Publish a web app to Azure with Visual Studio
- Introduction to .NET
- Create a new .NET project and work with dependencies
- Interactively debug .NET apps with the Visual Studio Code debugger
- Work with files and directories in a .NET app
- Introduction to Web Development with Blazor
- Build a web app with Blazor
- Interact with data in Blazor web apps
- Use pages, routing, and layouts to improve Blazor navigation
- Improve how forms and validation work in Blazor web apps
- Build rich interactive components with Blazor web apps
- Build reusable components with Blazor
- Build Connect Four game with Blazor
- Externalize the configuration of an ASP.NET app by using an Azure key vault
- Implement logging in a .NET Framework ASP.NET web application
- Improve session scalability in a .NET Framework ASP.NET web application by using Azure Cache for Redis
- Build a web API with minimal API, ASP.NET Core, and .NET
- Use a database with minimal API, Entity Framework Core, and ASP.NET Core
- Create a full stack application by using React and minimal API for ASP.NET Core
- Build your first microservice with .NET
- Deploy a .NET microservice to Kubernetes
- Create and deploy a cloudnative ASP.NET Core microservice
- Implement resiliency in a cloudnative ASP.NET Core microservice
- Instrument a cloudnative ASP.NET Core microservice
- Implement feature flags in a cloudnative ASP.NET Core microservices app
- Use managed data stores in a cloudnative ASP.NET Core microservices app
- Understand API gateways in a cloudnative ASP.NET Core microservices app
- Deploy a cloudnative ASP.NET Core microservice with GitHub Actions
- Contrast checkers
Someone who is color-blind might not be able to differentiate between colors, or might have difficulty working with colors that are similar to one another. The World Wide Web Consortium (W3C), the standards organization for the web, established a rating system for color contrast.
Choosing the right colors to ensure that your page is accessible to all can be tricky to do by hand. You can use the following tools to both generate appropriate colors and test your site to ensure compliance:
- Palette generation tools:
- Adobe Color, an interactive tool for testing color combinations
- Color Safe, a tool for generating text colors based on a selected background color
- Compliance checkers:
-
Browser extensions to test a page:
-
Applications:
-
- ARIA attributes
Someone who's using a screen reader would only hear the words description and order repeated without context.
To support these types of scenarios, HTML supports a set of attributes known as Accessible Rich Internet Applications (ARIA). You can use these attributes to provide more information to screen readers.
For example, you can use aria-label to describe a link when the format of the page doesn't allow you to. The description for widget might be set as:
<a href="#"aria-label="Widget description">description</a>
- Tutorial
https://github.com/MicrosoftDocs/mslearn-create-razor-pages-aspnet-core
dotnet --list-sdks
cd Contosopizza
dotnet watch
Ctrl + R : Reload
dotnet new page --name PizzaList --namespace ContosoPizza.Pages --output Pages
Observe the combination of HTML, Razor Syntax, and C# code in the file.
-
Razor Syntax is denoted by @ characters.
-
C# code is enclosed in @{ } blocks. Take note of the directives at the top of the file:
-
The @page directive specifies that this file is a Razor page.
-
The @model directive specifies the model type for the page (in this case, IndexModel, which is defined in Pages/Index.cshtml.cs).
-
The code sets the value of the Title item within the ViewData dictionary to "Home page".
-
The ViewData dictionary is used to pass data between the Razor page and the IndexModel class.
-
At runtime, the Title value is used to set the page's
<title>
element.
-
Separation of concerns:
Razor Pages enforces separation of concerns with a C# PageModel class, encapsulating data properties and logic operations scoped to its Razor page, and defining page handlers for HTTP requests. The PageModel class is a partial class that is automatically generated by the ASP.NET Core project template. The PageModel class is located in the Pages folder and is named after the Razor page. For example, the PageModel class for the Index.cshtml Razor page is named IndexModel.cs.
-
Use Razor Pages in your ASP.NET Core app when you:
-
Want to generate dynamic web UI.
-
Prefer a page-focused approach.
-
Want to reduce duplication with partial views.
Razor Pages simplifies ASP.NET Core page organization by keeping related pages and their logic together in their own namespace and directory.
Tag helpers
are used to address the inefficiencies of context switching between HTML and C#. Most of ASP.NET Core's built-in Tag helpers extend standard HTML elements. Tag helpers provide extra server-side attributes for HTML elements, making the elements more robust.
There are four tag helpers you should know for this project: Partial, Label, Input, and Validation Summary Message.
Use an asp-for attribute to specify a PageModel property.
<partial name="_ValidationScriptsPartial" />
<label asp-for="Foo.Id" class="control-label"></label>
<input asp-for="Foo.Id" class="form-control" />
<div asp-validation-summary="All"></div>
Representational State Transfer (REST) is an architectural style for building web services. REST requests are made over HTTP. They use the same HTTP verbs that web browsers use to retrieve webpages and send data to servers. The verbs are:
- GET: Retrieve data from the web service.
- POST: Create a new item of data on the web service.
- PUT: Update an item of data on the web service.
- PATCH: Update an item of data on the web service by describing a set of instructions about how the item should be modified. The sample application in this module doesn't use this verb.
- DELETE: Delete an item of data on the web service.
Build and test the web API
dotnet new webapi -f net6.0
Build and test the web API
dotnet run
Build the web API
dotnet build
.NET HTTP REPL command-line tool that you'll use to make HTTP requests to the web API.
dotnet tool install -g Microsoft.dotnet-httprepl
httprepl https://localhost:{PORT}
connect https://localhost:{PORT}
exit
ASP.Net Core Controller
using Microsoft.AspNetCore.Mvc;
namespace ContosoPizza.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
Visual Studio
-
Press F5 to build the project and run in debug mode.
-
Press Ctrl+F5 to build the project and run without attaching the debugger.
Razor is an ASP.NET syntax used to create dynamic web pages with C#.
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
The @page directive is telling ASP.NET to process this file as a Razor page.
The @model directive is telling ASP.NET to tie this Razor page with a C# class called PrivacyModel.
.NET is an ecosystem for application development
App model | Framework | Notes |
---|---|---|
Web | ASP.NET Core | The framework for building server-side logic. |
--- | --- | --- |
Web | ASP.NET Core MVC | The framework for building server-side logic for web pages or web APIs. |
Web | ASP.NET Core Razor Pages | The framework for building server-generated HTML. |
Web client | Blazor | Blazor is a part of ASP.NET Core. Its two modes allow for either Document Object Model (DOM) manipulation via sockets as a communication vehicle for running server-side code, or a WebAssembly implementation for running compiled C# on a browser. |
Desktop | WinForms | A framework for building "battleship gray" Windows-style applications. |
Desktop | Windows Presentation Foundation (WPF) | A framework for building dynamic desktop applications that conform to different form factors. WPF allows form elements to perform movement, fades, glides, and other effects with the help of a rich library of animations. |
Mobile | Xamarin | Allows .NET developers to build apps for iOS and Android devices. |
.NET has the fastest web framework on the planet according to TechEmpower benchmarks, an independent, open-source set of web performance benchmarks that measure dozens of languages and application frameworks.
A package dependency is a third-party library. It's a piece of reusable code that accomplishes something and can be added to your application.
https://www.nuget.org/packages/<package name>.
dotnet add package <package name>
-| bin/
---| Debug/
------| net3.1
--------| <files included in the dependency>
dotnet list package
dotnet list package --include-transitive
dotnet remove package <name of dependency>
Create a console application
dotnet new console -f net6.0
- Versioning
Major version: I'm OK with updating to the latest major version as soon as it's out. I accept the fact that I might need to change code on my end.
Minor version: I'm OK with a new feature being added. I'm not OK with code that breaks.
Patch version: The only updates I'm OK with are bug fixes.
- NuGet version ranges
1.0 x >= 1.0 Minimum version, inclusive
(1.0,) x > 1.0 Minimum version, exclusive
[1.0] x == 1.0 Exact version match
(,1.0] x ≤ 1.0 Maximum version, inclusive
(,1.0) x < 1.0 Maximum version, exclusive
[1.0,2.0] 1.0 ≤ x ≤ 2.0 Exact range, inclusive
(1.0,2.0) 1.0 < x < 2.0 Exact range, exclusive
[1.0,2.0) 1.0 ≤ x < 2.0 Mixed inclusive minimum and exclusive maximum version
(1.0) invalid invalid
dotnet list package
dotnet list package --outdated
- Logging and tracing
using System.Diagnostics;
Console.WriteLine("This message is readable by the end user.");
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");
Debug.WriteIf(count == 0, "Count should not be 0.");
- Work with the file system
using System.IO;
using System.Collections.Generic;
// List all content in a directory and all subdirectories
// Find all *.txt files in the stores folder and its subfolders
IEnumerable<string> allFilesInAllFolders = Directory.EnumerateFiles("stores", "*.txt", SearchOption.AllDirectories);
Console.WriteLine(Directory.GetCurrentDirectory());
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Console.WriteLine(Path.Combine("stores","201")); // outputs: stores/201
Console.WriteLine(Path.GetExtension("sales.json")); // outputs: .json
Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), "stores","201","newDir"));
bool doesDirectoryExist = Directory.Exists(filePath);
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "greeting.txt"), "Hello World!");
File.ReadAllText($"stores{Path.DirectorySeparatorChar}201{Path.DirectorySeparatorChar}sales.json");
Blazor is a user-interface framework built on .NET and Razor. Blazor applications can run on a server as part of an ASP.NET application, or can be deployed to run in the browser on a user's machine similar to a single-page-application.
-
Blazor Server is an implementation of the Blazor user-interface framework as part of the ASP.NET Core web development framework, deployed to a web server. Developing an application with Blazor Server generates HTML on a web server as it is requested by web site visitors, typically using a web browser. That HTML is then delivered to the visitor's browser, and a two-way communication pipeline is maintained using ASP.NET Core SignalR and preferring a Web Sockets connection.
-
Blazor WebAssembly, sometimes shortened to Blazor WASM, is an implementation of the Blazor user-interface framework that runs on the HTML 5 standard WebAssembly runtime present in all modern browsers. The binary output of your application, the DLL files, are transmitted to the browser and run with a version of .NET that has been optimized to work with the WebAssembly runtime regardless of the underlying operating system of the device browsing to the website.
Criteria | You could choose Blazor Server because... | You could choose Blazor WebAssembly because... |
---|---|---|
Developers are familiar with .NET | Building applications will feel like a familiar ASP.NET Core application | Building applications will use your existing skills to run natively in the browser |
--- | --- | --- |
Need to integrate with existing .NET investments | There is an existing model for integrating with ASP.NET Core applications | Allowing those resources to run natively in the browser gives better interaction and perceived performance than connecting to a web server |
Existing web servers | Your existing web servers are running ASP.NET Core | You need to deploy your application to any server without need for server-side rendering |
Complexity of the application | Your application has heavy processing requirements that benefit from distributed applications running in a data center | Your application can benefit from running with native processor speeds on the client |
Network requirements | Your application will always be connected to a server | Your application can run in 'occasionally connected' mode without requiring constant interaction with a server |
Code security requirements | Your application is validated or required to run in specific geographic locations that can be enforced with a server | Your application code can run anywhere on any device without this requirement |
Create a new Blazor app
dotnet new blazorserver -f net6.0
-| bin
-| Data
-| obj
-| Pages
-| _Host.cshtml
-| Counter.razor
-| Error.cshtml
-| Error.cshtml.cs
-| FetchData.razor
-| Index.razor
-| Properties
-| Shared
-| MainLayout.razor
-| MainLayout.razor.css
-| NavMenu.razor
-| NavMenu.razor.css
-| SurveyPrompt.razor
-| wwwroot
-| _Imports.razor
-| App.razor
-| appsettings.Development.json
-| appsettings.json
-| BlazorApp.csproj
-| Program.cs
Run the app
dotnet watch run
Razor
@page "/counter"
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
In this hierarchy of parent and child components, you can share information between them by using component parameters. You start by defining the component parameter in the child component. It's defined as a C# public property and decorated with the [Parameter] attribute:
<h2>New Pizza: @PizzaName</h2>
<p>@PizzaDescription</p>
@code {
[Parameter]
public string PizzaName { get; set; }
[Parameter]
public string PizzaDescription { get; set; } = "The best pizza you've ever tasted."
}
Data binding
Use data binding to connect an HTML element to a field, property, or expression. This way, when the value changes, the HTML element is automatically updated.
@page "/"
<h1>My favorite pizza is: @favPizza</h1>
<p>
Enter your favorite pizza:
<input @bind="favPizza" />
</p>
@code {
private string favPizza { get; set; } = "Margherita"
}
git clone https://github.com/MicrosoftDocs/mslearn-interact-with-data-blazor-web-apps.git BlazingPizza
Navigation
git clone https://github.com/MicrosoftDocs/mslearn-blazor-navigation.git BlazingPizza
Route parameters
@page "/FavoritePizzas/{favorite}"
<h1>Choose a Pizza</h1>
<p>Your favorite pizza is: @Favorite</p>
@code {
[Parameter]
public string Favorite { get; set; }
}
@inject NavigationManager NavigationManager
Layout
Layout components don't include a @page directive because they don't handle requests directly and shouldn't have a route created for them. Instead, the referencing components use the @page directive.
@inherits LayoutComponentBase
<header>
<h1>Blazing Pizza</h1>
</header>
<nav>
<a href="Pizzas">Browse Pizzas</a>
<a href="Toppings">Browse Extra Toppings</a>
<a href="FavoritePizzas">Tell us your favorite</a>
<a href="Orders">Track Your Order</a>
</nav>
@Body
<footer>
@new MarkdownString(TrademarkMessage)
</footer>
@code {
public string TrademarkMessage { get; set; } = "All content is © Blazing Pizzas 2021";
}
@page "/FavoritePizzas/{favorite}"
@layout BlazingPizzasMainLayout
<h1>Choose a Pizza</h1>
<p>Your favorite pizza is: @Favorite</p>
@code {
[Parameter]
public string Favorite { get; set; }
}
Event Handling
git clone https://github.com/MicrosoftDocs/mslearn-use-forms-in-blazor-web-apps.git BlazingPizza
EditForm
<EditForm Model=@currentForecast>
<InputNumber @bind-Value=currentForecast.TemperatureC width="5" min="-100" step="5"></InputNumber>
</EditForm>
<EditForm Model=Order.DeliveryAddress OnValidSubmit=PlaceOrder OnInvalidSubmit=ShowError>
Blazor component lifecycle
git clone https://github.com/MicrosoftDocs/mslearn-build-interactive-components-blazor.git BlazingPizza
Razor class libraries
By using Razor class libraries, you can share and reuse user-interface components between Blazor applications. In this module, you'll focus on building and sharing components for Blazor applications.
Configuration builders for ASP.NET
Implement logging in an ASP.NET web app
Store session state information in an ASP.NET web app
git clone https://github.com/dotnet-architecture/eShopModernizing.git
log4net
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="file" />
</root>
<appender name="file" type="log4net.Appender.RollingFileAppender">
<file value="logFiles\myapp.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %property{activity} %level %logger - %property{requestinfo}%newline%message%newline%newline" />
</layout>
</appender>
</log4net>
Swagger
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "PizzaStore API",
Description = "Making the Pizzas you love",
Version = "v1" });
});
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "PizzaStore API V1");
});
app.MapGet("/", () => "Hello World!");
app.Run();
git clone https://github.com/microsoftdocs/mslearn-dotnet-kubernetes
- Cloud Native Microservice: eShopOnContainers Solution architecture
Solution diagram
https://github.com/dotnet-architecture/eshoponcontainers
Free e-book: .NET Microservices: Architecture for Containerized .NET Applications
To implement code-based resiliency, this module uses Polly, which is a .NET library for resilience and transient failure handling.
Some popular service mesh options for Kubernetes clusters include Linkerd, Istio, and Consul.
Video: Implement microservices patterns with .NET and Docker containers
- Enable Azure Monitor for Containers
az aks enable-addons \
--addons monitoring \
--name eshop-learn-aks \
--resource-group eshop-learn-rg \
--query provisioningState
-
Blazor Web Server
@Link
using BlazorAppChatGPTPluginTEST.Data; using Microsoft.Extensions.FileProviders; using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton<WeatherForecastService>(); // ** Create CORS policy called OpenAI // to allow OpenAI to access the API builder.Services.AddCors(options => { options.AddPolicy("OpenAI", policy => { policy.WithOrigins( "https://chat.openai.com", "http://localhost:5200") .AllowAnyHeader() .AllowAnyMethod(); }); }); // ** Add controllers to the application builder.Services.AddControllers(); // ** Add Swagger/OpenAPI to the application // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["controller"]}_{e.HttpMethod}"); options.SwaggerDoc("v1", new OpenApiInfo { Title = "Blazor Azure Cognitive Search Plugin", Version = "v1", Description = "Blazorで動作するAzure Cognitive Search接続用のプラグインです。" }); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } // Disable this so that OpenAI can access the API // without using https (which is not supported by OpenAI) //app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.MapBlazorHub(); app.MapControllers(); app.MapFallbackToPage("/_Host"); // ** CORS to allow OpenAI to access the API app.UseCors("OpenAI"); // ** Serve the .well-known folder for OpenAI app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider( Path.Combine(Directory.GetCurrentDirectory(), ".well-known")), RequestPath = "/.well-known" }); // ** UseSwagger app.UseSwagger(c => { c.PreSerializeFilters.Add((swaggerDoc, httpReq) => { swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}" } }; }); }); // ** UseSwaggerUI app.UseSwaggerUI(x => { x.SwaggerEndpoint( "/swagger/v1/swagger.yaml", "Blazor TODO Plugin (no auth)" ); }); app.Run();