Backend Development Guide - ganweisoft/WebPlugins GitHub Wiki
The template engine contains basic templates for plugin development. Use the following command to download the template engine from the Ganwei NuGet package manager to your local environment.
dotnet new -i IoTCenterCore.ProjectTemplates::6.1.1 --nuget-source https://nuget.ganweicloud.com
You can create a plugin template solution via command line or Visual Studio. The naming convention for the plugin project is: [Company Abbreviation]+IoTCenter.Module+[Plugin Name]
dotnet new iot --name [Plugin Name]
For example, to create the Ganweisoft.IoTCenter.Module.Test plugin, execute the following command:
dotnet new iot --name Ganweisoft.IoTCenter.Module.Test
Process as follows:
Implicit import of common namespaces in C# 10.0 (refer to https://learn.microsoft.com/zh-cn/dotnet/core/tutorials/top-level-templates#implicit-using-directives)
This folder mainly provides Markdown records related to the plugin interface, database scripts, and version updates, etc., for reference only.
This folder mainly provides unified package management and upgrades. AspNetCore official packages are placed in the Dependencies.AspNetCore.props file, while other packages are in the Dependencies.props file.
- Controllers: Controller interfaces
- Data: Entity models
- Dto: Cross-plugin operation models
- IntegrationEvents: Event bus
- Mappers: AutoMapper mapping configurations
- Models: Data Transfer Objects (DTOs)
- Services: Service interfaces
- ServicesImpl: Implementations of service interfaces
- .json: Plugin configuration (the configuration name should match the plugin name, do not change the name arbitrarily)
- Manifest: Plugin assembly information
- Startup: Registration interfaces, use of middleware, etc.
- TestDbContext: EntityFramework Core context
- Do not define composite primary keys, nor is it recommended to use foreign keys.
- Due to different database sensitivities to case, it is strongly recommended to uniformly map to lowercase.
- Unless there are special circumstances, it is strongly recommended to use EF Core annotations, i.e., DataAnnotation.
Convention: Suffix of the plugin name + DbContext. For example, for the Ganweisoft.IoTCenter.Module.UserDemo plugin we created, the context name is defined as UserDemoDbContext.
-
Do not delete GwDbContext; keep the default inheritance of GwDbContext. This context includes operations on underlying data models such as product tables, device tables, telemetry tables, remote signaling, etc.
-
Overwrite the OnModelCreating method; do not delete base.OnModelCreating(modelBuilder).
The response data structure must be wrapped and returned through the OperateResult exposed by the underlying layer, converting the operating entity into a DTO mapping. The DTO model is stored in the Models directory, and the naming convention is [ClassName]+Model.
Directly operate the database model through the context injected by the constructor.
Add, delete, query, update conventional basic operations, for example, the AddAsync
method has already been implemented in the base class.
Register services and EntityFramework Core context in the Startup
class file.
The IoT platform uses JWT or Cookie authentication authorization. All defined controllers must inherit from DefaultController; otherwise, an exception or 401 will be thrown when accessing the interface. Use the registered service interface through the constructor, and the request method is recommended to follow the RESTful style.
Attach the plugin to the Web API service process (for a single gateway, attach to the GWHost1 service process), and then you can set breakpoints to debug specific interfaces through the UI interface.
After successfully logging into the IoTCenter platform with the correct account and password, press F12 in the browser to view any non-anonymous interface, and then copy the Cookie information from the request header.
For example, we test the user addition interface:
Log output for Web API and gateway service debugging is uniformly managed through the logsetting.json file located under /IoTCenterWeb/publish. The platform uses Serilog to manage log files.
To debug detailed log outputs of related services and plugins before and after the normal startup of Web API, modify the minimum level in the log configuration file to Debug.
Details
We expose the ILoggingService logging interface located in the IoTCenter.Utilities namespace for logging debugging, business logs, and storage of key operations records in actual business scenarios.
The context parameter included in the overloaded function can be used as the exclusive log file output for plugins. We standardize the context as the plugin assembly name.
When performing important operations on the platform, we need to record these actions in detail for auditability and traceability. For example, remote access IP and port, user account, resources operated, events performed, and results after the operation. We use the audit method provided in the logging interface (Audit) for detailed recording.
We provide string extension methods located in the IoTCenter.Utilities.Extensions namespace. Examples include checking for empty strings (IsEmpty), splitting strings (SplitString), splitting strings and removing duplicates (SplitStringWithDistinct), splitting strings and converting to int arrays (SplitStringToIntArray), converting strings to int (ToInt), strings to decimal (ToDecimal), strings to DateTime (ToDateTime), strings to double (ToDouble), etc.
We provide date extension methods located in the IoTCenter.Utilities.Extensions namespace. Examples include converting dates to Unix timestamps (ToUnixTimestamp), converting Unix timestamps back to dates (FromUnixTimeStampMillisecond), converting dates to seconds (GetUnixTimeStampSeconds), converting dates to milliseconds (GetUnixTimeStampMilliseconds), etc.
We offer serialization extension methods located in the IoTCenter.Utilities namespace. Examples include converting objects to JSON strings (ToJson), and deserializing JSON strings back to objects (FromJson), etc.
We provide enumeration extension methods located in the IoTCenter.Utilities namespace. Examples include retrieving description information of enumerations (GetDescription), getting display names of enumerations (GetDisplayName), etc.
We offer key-value pair attribute data structures commonly involved in front-end and back-end interactions or storage within the IoTCenter.Utilities namespace. Examples include key-value pairs requiring Id and Name attributes (IdNameObject), key and value attributes (KeyValueObject), and name and value attributes (NameValueObject).
Details
We provide a Session class located in the System namespace that is injected via a constructor to obtain user-related information from each request context, such as the current operating account, role name, whether it's an admin, and access IP and port information.
A top-level root node must be defined, and it is recommended to use the full plugin name or suffix name as the top-level root node. Otherwise, when multiple plugins are loaded and read nodes with the same name, they will be overwritten. For example, reading the Url value under the following root node.
The underlying layer exposes the IIotConfiguration interface, which is injected through the constructor and used similarly to the IConfiguration interface.
Using the https://github.com/atifaziz/NCrontab library to implement scheduled job expressions, including second-level definitions that support six-part expression definitions (the underlying job rolling cycle is 10 seconds, so custom scheduled jobs should be >= 10 seconds).
The underlying layer exposes the IBackgroundTask
interface, which configures Cron expressions through the BackgroundTask
attribute.
Scheduled tasks are injected as singletons.
In practical use, there may be cases where scheduled tasks run only once or do not run at all, based on the underlying logic of scheduled tasks.
Here are three points to note:
- Plugin scheduled tasks use the
[Plugin Assembly Name + Scheduled Task Class Name]
as the plan name, so plan names in different plugins must not be repeated. - The
DoWorkAsync
method call is automatically invoked by the underlying layer according to the rolling cycle, and it should not be called again within its method using While, otherwise it will completely block other task calls. - It is recommended to use asynchronous methods for
DoWorkAsync
, otherwise, if the business logic takes too long, it will block other task calls.
To perform DTO conversion, various packages exist. Based on this, the underlying layer standardizes the use of AutoMapper for mapping. AutoMapper is not detailed here; if unfamiliar, please consult relevant materials and understand it. To consider different scenarios or simplify reasons, the underlying layer provides multiple mapping usage methods.
We use the following source and target objects as an example demonstration.
The underlying layer exposes the IAutoMapperConfig
interface, creating a mapping configuration class implementing this interface. In this class, globally configure all mappings within the plugin library, and recommend naming the configuration class as [Plugin Name + AutoMapperConfig]
.
AutoMapper ignores property mappings using:
.ForMember(d => d.Id, o => o.Ignore())
To further simplify writing, the underlying layer additionally extends and encapsulates the Ignore
method:
.Ignore(d => d.Id)
For example, as shown below, we only configure the mapping property Name
, but ignore mapping Id
.
The underlying layer exposes the IObjectMapping
interface for object mapping, as follows using constructor injection.
Call the Map
method in the interface to complete data mapping from the source object to the target object. This method is the built-in mapping method of AutoMapper.
Similarly, use Postman to simulate and verify testing.
The above is the default mapping method of the AutoMapper framework, but the underlying layer additionally provides extended methods based on object
for more convenience and simplicity, so we no longer map through the IObjectMapping
interface.
For example, the above maps the source object CreateUserDTO
to the target object UserTest
by importing the namespace IoTCenterCore.AutoMapper
, then calling the MapTo
extension method, which can be modified as follows:
Call the MapToList
method to map the source collection to the target collection.