Frp (fast reverse proxy) is an open-source tunneling tool: a server with a public IP runs frps, machines behind NAT run frpc, and the two keep a persistent connection. Traffic hitting ports on the public server is forwarded through that connection to internal services. Typical use case: exposing a web service or SSH on your home machine to the outside world. Configuration files use TOML (frps.toml / frpc.toml; the INI format is deprecated since v0.52+). ...
About the EditorConfig File
EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable the editors to read the file format and adhere to the defined styles. EditorConfig files are easy to read and work well with version control systems. https://editorconfig.org Visual Studio 2019 and later support EditorConfig files, so you can use an EditorConfig file in Visual Studio to define and maintain code style settings. ...
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. ...
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. ...
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. ...
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
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. ...
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. ...
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. ...
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. ...