Adding File Header Comments to the Project

File Header Comments A file header comment is a comment used to state a file’s copyright and license. It usually includes the copyright notice, license information, author information, and other related details. File header comments are a good practice that helps developers understand the copyright and licensing information of a file. Copyright Declaration Convention // Copyright (c) HelloShop Corporation. All rights reserved. // See the license file in the project root for more information. namespace HelloShop.AppHost { internal class Class1 { } } About the EditorConfig Configuration File It maintains a consistent coding style across multiple developers working on the same project in different editors and IDEs. ...

March 10, 2026

Aggregation OpenApi documentation

Each microservice has its own OpenApi document, but in actual development, we prefer to aggregate the OpenApi documents of all microservices together for easy viewing and debugging. Microservices are all developed based on the Aspire framework, so we can use the service discovery function provided by the Aspire framework to automatically aggregate the OpenApi documents of all microservices. Automatically configure OpenApi documentation using Aspire Service Discovery public class OpenApiConfigureOptions() : IConfigureOptions<SwaggerUIOptions> builder.Services.AddTransient<IConfigureOptions<SwaggerUIOptions>, OpenApiConfigureOptions>(); Customize OpenApi document styles Create the Custom.css file under the Resource/OpenApi folder. ...

March 10, 2026

API Gateway Aggregation

Use a gateway to aggregate multiple individual requests into a single request. This pattern is useful when a client must make multiple calls to different backend systems to perform an operation. Context and problem In some cases, a client needs to make multiple requests to multiple backend systems. For example, a client may need to retrieve data from multiple services and then aggregate that data into a single response. In this situation, the client must issue multiple requests, which can lead to performance problems. In addition, the client must also handle multiple responses, which can lead to complexity problems. ...

March 10, 2026

Authenticating with JwtBearer Tokens

Introduction JwtBearer token authentication is a JSON Web Token-based authentication method used to verify a user’s identity. It is a stateless authentication approach well suited to Web APIs and web applications. Installing the NuGet package dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer Building your own Identity API endpoints builder.Services.AddIdentity<User, Role>(options => { options.SignIn.RequireConfirmedAccount = false; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequiredLength = 5; }).AddEntityFrameworkStores<IdentityServiceDbContext>(); Validating tokens with JwtBearer builder.Services.AddAuthentication(options => { options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = CustomJwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters.ValidateIssuer = false; options.TokenValidationParameters.ValidateAudience = false; options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(issuerSigningKey)); }) A custom authentication handler public class CustomJwtBearerDefaults public class CustomJwtBearerOptions public class CustomJwtBearerHandler public class CustomJwtBearerExtensions Configuring token generation builder.Services.AddAuthentication().AddJwtBearer().AddCustomJwtBearer(options => { options.IssuerSigningKey = issuerSigningKey; options.SecurityAlgorithm = SecurityAlgorithms.HmacSha256; }); Running the pgAdmin management tool in a container docker pull dpage/pgadmin4 docker run --name pgadmin -e PGADMIN_DEFAULT_EMAIL=test@test.com -e PGADMIN_DEFAULT_PASSWORD=test -e TZ=Asia/Shanghai -d -p 5050:80 dpage/pgadmin4

March 10, 2026

Automatic mapping of entities and models

Automatically map entities and models using AutoMapper AutoMapper is an object mapping tool that can automatically map entity objects and model objects, reducing the workload of manual mapping and improving development efficiency. In addition, Mapster is also a high-performance object mapping tool that supports source generation code and has better performance. AutoMapper is more popular, more mature, more stable, easier to use, more flexible, more powerful, more comprehensive, and more popular than Mapster. AutoMapper is part of the .NET Foundation, an open source project, and a very excellent object mapping tool. ...

March 10, 2026

Automatic Migration and Database Seeding

Generating migration scripts dotnet ef migrations add InitialCreate --output-dir Infrastructure/Migrations Applying the latest migration to the database manually dotnet ef database update Creating the database with code await dbContext.Database.EnsureCreatedAsync(); EnsureCreatedAsync creates the database if it does not exist and creates all the tables, but it does not apply migration scripts. It is suitable for development environments. Applying the latest migration to the database with code await dbContext.Database.MigrateAsync(); MigrateAsync applies migration scripts and creates the database first if it does not exist. It is suitable for production environments. ...

March 10, 2026

Automatic Model Validation

Model validation is an important feature in ASP.NET Core MVC that helps us verify that the data entered by users matches expectations. Validation with data annotations See the Microsoft data annotations documentation. public class User { [Required] [StringLength(32)] public string Name { get; set; } [Required] [EmailAddress] public string Email { get; set; } } Validation with fluent chaining FluentValidation is a .NET library for building type-safe validation rules. Its design goal is to provide a simple, clear API while still supporting complex validation rules. ...

March 10, 2026

Best Practices for Resource-Based Authorization

Providing a Permission Check Endpoint in the Identity Service [HttpHead] public async Task<IActionResult> CheckPermission(string permissionName, string? resourceType = null, string? resourceId = null { if (await permissionChecker.IsGrantedAsync(permissionName, resourceType, resourceId)) { return Ok(); } return Forbid(); } Remote Permission Checker Dictionary<string, string?> parameters = new() { [nameof(name)] = name, [nameof(resourceType)] = resourceType, [nameof(resourceId)] = resourceId }; string queryString = QueryHelpers.AddQueryString(string.Empty, parameters); HttpRequestMessage request = new(HttpMethod.Head, queryString); using HttpResponseMessage response = await httpClient.SendAsync(request); return response.IsSuccessStatusCode; Reimplementing the Permission Handler public class PermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler<OperationAuthorizationRequirement> { protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement) { if (context.Resource is IAuthorizationResource resource) { if (await permissionChecker.IsGrantedAsync(context.User, requirement.Name, resource.ResourceType, resource.ResourceId)) { context.Succeed(requirement); } else { context.Fail(); } return; } if (await permissionChecker.IsGrantedAsync(context.User, requirement.Name)) { context.Succeed(requirement); return; } context.Fail(); } } Invalidating the Cache for Testing await distributedCache.SetObjectAsync(cacheKey, new PermissionGrantCacheItem(isGranted), new DistributedCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now }); Readability Refactoring Regenerate the demo data and rename Name in authorization to PermissionName, which is more readable. ...

March 10, 2026

Configuring Distributed Events with Dapr Pub/Sub

Dapr Technical Architecture https://docs.dapr.io/concepts/overview Installing the Dapr CLI Scaffolding Tool https://docs.dapr.io/zh-hans/getting-started/install-dapr-cli Initializing the Dapr Runtime with Containers https://docs.dapr.io/zh-hans/getting-started/install-dapr-selfhost dapr init Initializing the Dapr Runtime Offline Without Containers dapr init -s Initializing the Dapr Runtime from an Offline Bundle https://docs.dapr.io/zh-hans/operations/hosting/self-hosted/self-hosted-airgap/ dapr init --from-dir C:\daprbundle Opening the Default Initialization Directory explorer "$env:USERPROFILE\.dapr" explorer "%USERPROFILE%\.dapr" Viewing Dapr Runtime Status with the Dashboard dapr dashboard Configuring Distributed Events with Pub/Sub /components/redis-pubsub.yaml Redis-Based Pub/Sub Configuration apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: pubsub spec: type: pubsub.redis version: v1 metadata: - name: redisHost value: localhost:6379 - name: redisPassword value: "" RabbitMQ-Based Pub/Sub Configuration /components/rabbitmq-pubsub.yaml apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: pubsub spec: type: pubsub.rabbitmq version: v1 metadata: - name: host value: "amqp://guest:guestpwd@localhost:5672" - name: durable value: "false" - name: deletedWhenUnused value: "false" - name: autoAck value: "false" - name: reconnectWait value: "0" - name: concurrency value: parallel Starting the Sidecar and Application with the Dapr Command dapr run --app-id myapp --dapr-http-port 3500 --dapr-grpc-port 50001 --app-port 5000 --log-level debug --config ./configuration/config.yaml --components-path ./components dotnet run Starting the Sidecar and Application with Aspire dotnet add package Aspire.Hosting.Dapr var pubsub = builder.AddDaprPubSub("pubsub", new DaprComponentOptions { LocalPath = "./DaprComponents/" }); var productService = builder.AddProject<Projects.HelloWorld_ProductService>("productservice") .WithReference(identityService) .WithDaprSidecar() .WithReference(pubsub); RabbitMQ Pub/Sub Configuration var username = builder.AddParameter("username", secret: true); var password = builder.AddParameter("password", secret: true); var messaging = builder.AddRabbitMQ("messaging", username, password); // Service consumption builder.AddProject<Projects.ExampleProject>() .WithReference(messaging);

March 10, 2026

Creating Code Repositories

Warehouse naming convention Create a code repository named HelloShop on Github. Regarding the naming convention of the code repository, use the SnakeCaseLower nomenclature. Naming policy Original Converted PascalCase HelloShop HelloShop CamelCase HelloShop helloShop SnakeCaseLower HelloShop hello_shop SnakeCaseUpper HelloShop HELLO_SHOP KebabCaseLower HelloShop hello-shop KebabCaseUpper HelloShop HELLO-SHOP Warehouse folder structure Create a folder named src in the repository to store the source code. Documentation Create a file named README.md in the warehouse to store the warehouse documentation. Of course, each folder should have a documentation if possible. ...

March 10, 2026