HeadlessPluginApi 1.0.1
dotnet add package HeadlessPluginApi --version 1.0.1
NuGet\Install-Package HeadlessPluginApi -Version 1.0.1
<PackageReference Include="HeadlessPluginApi" Version="1.0.1" />
<PackageVersion Include="HeadlessPluginApi" Version="1.0.1" />
<PackageReference Include="HeadlessPluginApi" />
paket add HeadlessPluginApi --version 1.0.1
#r "nuget: HeadlessPluginApi, 1.0.1"
#:package HeadlessPluginApi@1.0.1
#addin nuget:?package=HeadlessPluginApi&version=1.0.1
#tool nuget:?package=HeadlessPluginApi&version=1.0.1
🔌 HeadlessPluginApi
Thư viện Plugin Core - Tích hợp với nền tảng Low-Code Headless của Thần Nông
📋 Mục Lục
- Giới Thiệu
- Tính Năng Chính
- Kiến Trúc
- Yêu Cầu Hệ Thống
- Cài Đặt
- Hướng Dẫn Sử Dụng
- Tính Năng Nâng Cao
- API Endpoints
- Debugging Plugins
- Ví Dụ Mẫu
- Đóng Góp
- License
🎯 Giới Thiệu
HeadlessPluginApi là thư viện Core Plugin cho phép developer tự do phát triển các plugin độc lập với host application. Plugin sau khi phát triển xong có thể triển khai trên host app như một module có thể addon vào hệ thống chính.
Tại sao cần HeadlessPluginApi?
| Vấn Đề | Giải Pháp của HeadlessPluginApi |
|---|---|
| ❌ Monolith khó mở rộng | ✅ Plugin-based architecture - Tách biệt logic nghiệp vụ |
| ❌ Deploy lại toàn bộ app khi thay đổi nhỏ | ✅ Hot reload/unload - Load plugin mới không cần restart |
| ❌ Khó kiểm soát quyền truy cập | ✅ JsonLogic Authorization - Phân quyền linh hoạt theo context |
| ❌ Thiếu chuẩn hoá API | ✅ Auto schema generation - Tự động tạo OpenAPI schema |
| ❌ Vendor lock-in | ✅ Isolated AssemblyLoadContext - Plugin hoàn toàn độc lập |
✨ Tính Năng Chính
1. Plugin Lifecycle Management
- ✅ Load/Unload/Reload plugins động
- ✅ Isolated AssemblyLoadContext (McMaster.NETCore.Plugins)
- ✅ Version-based DLL naming (
PluginName_1.0.0.dll) - ✅ Auto-discovery từ bin folder (Debug mode)
- ✅ Manifest support (
plugin.json)
2. Command Execution System
- ✅ Strongly-typed parameters - Tự động deserialize từ JSON
- ✅ Async/await support -
Task<object?>return type - ✅ DI injection -
IServiceProvider,UserSessionContext,CancellationToken - ✅ Qualified command names -
PluginName.CommandNameđể tránh conflict - ✅ Scoped service provider - Mỗi execution có scope riêng
3. Authorization & Security
- ✅ [RequireAuth] attribute - Declarative authorization
- ✅ JsonLogic evaluation - Flexible rule-based authorization
- ✅ Command-level security - Phân quyền chi tiết từng command
- ✅ UserSessionContext integration - Tích hợp với hệ thống người dùng
4. Schema Auto-Generation
- ✅ [CommandParameter] attribute - Metadata cho tham số
- ✅ Auto-generate OpenAPI schema - Từ method signature
- ✅ IntelliSense support - XML docs trong schema
- ✅ Swagger UI integration - Try-it-out UI tự động
5. Developer Experience
- ✅ Zero-config debug mode - Project reference + breakpoint support
- ✅ DiscoveryHostApi - Standalone debug environment
- ✅ In-memory implementations - Không cần MongoDB/Redis cho dev
- ✅ Template download - Sample plugin qua API
🏗️ Kiến Trúc
┌─────────────────────────────────────────────────────────────────┐
│ Host Application │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ PluginEngine (IPluginEngine) │ │
│ │ • Load/Unload/Reload lifecycle │ │
│ │ • Metadata persistence (MongoDB) │ │
│ └───────────┬─────────────────────────────────┬─────────────┘ │
│ │ │ │
│ ┌───────────▼──────────┐ ┌──────────▼──────────────┐ │
│ │ PluginLoader │ │ PluginRegistry │ │
│ │ (McMaster.Plugins) │ │ (Thread-safe cache) │ │
│ │ • Isolated ALC │ │ • GetPlugin() │ │
│ │ • Auto-discovery │ │ • Resolve(command) │ │
│ └──────────────────────┘ └─────────────────────────┘ │
│ │ │
│ ┌───────────▼──────────────┐ │
│ │ PluginDispatcher │ │
│ │ • Route commands │ │
│ │ • Authorization check │ │
│ │ • Scoped DI │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
┌──────────▼──────────┐
│ Plugin DLL │
│ ┌──────────────┐ │
│ │ PluginEntry │ │
│ │ (IPluginEntry)│ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ Commands │ │
│ │ (IPluginCmd) │ │
│ └──────────────┘ │
└─────────────────────┘
Luồng Thực Thi Command
1. HTTP Request → POST /api/plugin/execute
{
"command": "DemoCalculator.add",
"payload": { "a": 10, "b": 5 }
}
2. PluginDispatcher.ExecuteAsync()
├─ Resolve plugin & command → PluginRegistry
├─ Authorization check → JsonLogicAuthEvaluator
├─ Create scoped ServiceProvider
├─ Bind parameters (UserSessionContext, payload, IServiceProvider, CancellationToken)
└─ Invoke command.ExecuteAsync() via reflection
3. Command Execution
public async Task<object?> ExecuteAsync(
UserSessionContext ctx,
int a,
int b,
IServiceProvider services,
CancellationToken ct)
{
var logger = services.GetRequiredService<ILogger<AddCommand>>();
logger.LogInformation("Adding {A} + {B}", a, b);
return new { result = a + b };
}
4. Response → PluginExecuteResponse
{
"success": true,
"result": { "result": 15 },
"executionTimeMs": 12
}
💻 Yêu Cầu Hệ Thống
- .NET 8 SDK trở lên
- Visual Studio 2022 hoặc VS Code (với C# extension)
- Headless.SharedServices (core dependency)
📦 Cài Đặt
1. Cài Đặt NuGet Package
dotnet add package HeadlessPluginApi --version 1.0.1
Hoặc trong .csproj:
<ItemGroup>
<PackageReference Include="HeadlessPluginApi" Version="1.0.1" />
</ItemGroup>
2. Cài Đặt Dependencies
# Core dependency (bắt buộc)
dotnet add package Headless.SharedServices
# Optional dependencies (nếu chưa có)
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Hosting
🚀 Hướng Dẫn Sử Dụng
1. Tạo Plugin Đơn Giản
Bước 1: Tạo Plugin Entry
using HeadlessPluginApi.Abstractions;
public class MyPlugin : PluginEntryBase
{
protected override void OnLoad(IServiceProvider hostServices)
{
Logger?.LogInformation("MyPlugin loading...");
}
public override void OnInit()
{
Logger?.LogInformation("MyPlugin initialized with {Count} commands",
GetCommands().Count);
}
public override void OnUnload()
{
Logger?.LogInformation("MyPlugin unloading...");
}
}
Bước 2: Thêm Assembly Metadata (cho auto-detect Name/Version)
<PropertyGroup>
<AssemblyTitle>MyPlugin</AssemblyTitle>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0</AssemblyVersion>
</PropertyGroup>
2. Định Nghĩa Commands
Command Đơn Giản
using HeadlessPluginApi.Abstractions;
using Headless.Contracts.Models.Users;
public class AddCommand : PluginCommand
{
public override string CommandName => "add";
public override string Description => "Cộng hai số";
public async Task<object?> ExecuteAsync(
UserSessionContext context,
int a,
int b,
CancellationToken cancellationToken)
{
return new
{
result = a + b,
operation = "add",
executedBy = context.user?.userName ?? "Anonymous"
};
}
}
Command với DI Services
public class SaveUserCommand : PluginCommand
{
public override string CommandName => "save_user";
public async Task<object?> ExecuteAsync(
UserSessionContext context,
string username,
string email,
IServiceProvider services,
CancellationToken cancellationToken)
{
// Resolve services từ host
var db = services.GetRequiredService<IMultiScopeDBRepository>();
var logger = services.GetRequiredService<ILoggerFactory>()
.CreateLogger<SaveUserCommand>();
logger.LogInformation("Saving user: {Username}", username);
var user = new User { Username = username, Email = email };
await db.InsertOne(user, cancellationToken: cancellationToken);
return new { success = true, userId = user.Id };
}
}
3. Tích Hợp vào Host Application
Bước 1: Register Plugin Module
// Program.cs (ASP.NET Core)
using HeadlessPluginApi.Hosting;
var builder = WebApplication.CreateBuilder(args);
// ✅ Register plugin services
builder.Services.AddPluginModule(options =>
{
options.pluginFolder = Path.Combine(builder.Environment.ContentRootPath, "Plugins");
options.autoLoadOnStartup = true;
options.enableManagementApi = true;
});
// ✅ Register host services (plugins có thể dùng)
builder.Services.AddSingleton<IMultiScopeDBRepository, MyDbRepo>();
builder.Services.AddSingleton<ICacheRepository, InMemoryCacheRepo>();
var app = builder.Build();
// ✅ Map plugin endpoints
app.MapPluginEndpoints();
app.Run();
Bước 2: Upload Plugin
# Build plugin
cd MyPlugin
dotnet build -c Release
# Upload qua API
curl -X POST http://localhost:5000/api/plugin/upload \
-F "file=@bin/Release/net8.0/MyPlugin.dll"
Bước 3: Load Plugin
curl -X POST http://localhost:5000/api/plugin/load \
-H "Content-Type: application/json" \
-d '{"pluginPath": "MyPlugin"}'
4. Thực Thi Commands
Via HTTP API
curl -X POST http://localhost:5000/api/plugin/execute \
-H "Content-Type: application/json" \
-d '{
"command": "MyPlugin.add",
"payload": { "a": 10, "b": 5 }
}'
Response:
{
"success": true,
"result": {
"result": 15,
"operation": "add",
"executedBy": "admin@example.com"
},
"executionTimeMs": 8
}
Via C# Code
public class MyService
{
private readonly IPluginDispatcher _dispatcher;
private readonly UserSessionContext _ctx;
public async Task<object?> ExecutePluginCommand()
{
var payload = JsonDocument.Parse(@"{""a"": 10, ""b"": 5}").RootElement;
var result = await _dispatcher.ExecuteAsync(
"MyPlugin.add",
payload,
_ctx,
CancellationToken.None);
return result;
}
}
🔐 Tính Năng Nâng Cao
Authorization (JsonLogic)
Định Nghĩa Quyền Trên Command
using HeadlessPluginApi.Authorization;
public class DeleteUserCommand : PluginCommand
{
public override string CommandName => "delete_user";
// ✅ Chỉ admin mới được xóa user
[RequireAuth(
rule: @"{
""==="": [
{""var"": ""user.role""},
""admin""
]
}",
message: "Chỉ admin mới có quyền xóa người dùng")]
public async Task<object?> ExecuteAsync(
UserSessionContext context,
string userId,
CancellationToken cancellationToken)
{
// Implementation
}
}
Authorization Check Flow
1. PluginDispatcher.ExecuteAsync()
│
2. PluginAuthorizationEvaluator.EvaluateCommandAsync()
│
├─ Check [RequireAuth] attribute
│
├─ Build JsonLogic context:
│ {
│ "user": { "userId": "abc", "role": "admin", ... },
│ "payload": { "userId": "user123" },
│ "command": "delete_user"
│ }
│
├─ Evaluate rule với JsonLogic.Net
│ {"===": [{"var": "user.role"}, "admin"]}
│
└─ Return AuthorizationResult
• Succeeded = true → Continue execution
• Succeeded = false → Throw PluginAuthorizationException (403)
Ví Dụ Rules Phức Tạp
// Rule 1: Chỉ admin hoặc chính chủ mới được sửa
[RequireAuth(
rule: @"{
""or"": [
{""==="": [{""var"": ""user.role""}, ""admin""]},
{""==="": [{""var"": ""user.userId""}, {""var"": ""payload.userId""}]}
]
}"]
// Rule 2: Chỉ được thực hiện trong giờ hành chính (8-17h)
[RequireAuth(
rule: @"{
""and"": [
{"">="": [{""var"": ""context.hour""}, 8]},
{""<="": [{""var"": ""context.hour""}, 17]}
]
}")]
Auto Schema Generation
Thêm Metadata Cho Parameters
using HeadlessPluginApi.Attributes;
public class CreateOrderCommand : PluginCommand
{
public override string CommandName => "create_order";
public async Task<object?> ExecuteAsync(
UserSessionContext context,
[CommandParameter(
description: "Mã khách hàng",
required: true,
example: "CUST001")]
string customerId,
[CommandParameter(
description: "Danh sách sản phẩm (product_id và quantity)",
required: true)]
List<OrderItem> items,
[CommandParameter(
description: "Ghi chú đơn hàng",
required: false)]
string? notes,
CancellationToken cancellationToken)
{
// Implementation
}
}
Lấy Schema Qua API
GET /api/plugin/commands/MyPlugin.create_order/schema
Response:
{
"success": true,
"pluginName": "MyPlugin",
"commandName": "create_order",
"parameterSchema": {
"parameters": [
{
"name": "customerId",
"type": "string",
"description": "Mã khách hàng",
"required": true,
"example": "CUST001"
},
{
"name": "items",
"type": "array",
"description": "Danh sách sản phẩm (product_id và quantity)",
"required": true,
"itemType": "OrderItem"
},
{
"name": "notes",
"type": "string",
"description": "Ghi chú đơn hàng",
"required": false
}
]
},
"authorization": {
"requiresAuth": false
}
}
Version Management
Quy Ước Naming
✅ ĐÚNG (recommended):
MyPlugin_1.0.0.dll
MyPlugin_1.0.1.dll
MyPlugin_1.1.0.dll
MyPlugin_2.0.0.dll
⚠️ Hỗ trợ (legacy):
MyPlugin.20250116120000000.dll (timestamp format)
MyPlugin.dll (fallback - không khuyến nghị)
Upload Workflow
# Version 1.0.0 (initial)
POST /api/plugin/upload
→ Uploaded: MyPlugin_1.0.0.dll ✅
# Version 1.0.0 again (duplicate)
POST /api/plugin/upload
→ 409 Conflict: Version 1.0.0 already exists ❌
# Version 1.1.0 (upgrade)
POST /api/plugin/upload
→ Uploaded: MyPlugin_1.1.0.dll ✅
# Reload để dùng version mới
POST /api/plugin/reload/MyPlugin
→ Loads MyPlugin_1.1.0.dll (latest version)
Version Selection Logic
// PluginLoader tự động chọn DLL theo thứ tự:
1. Highest semantic version (1.2.0 > 1.1.0 > 1.0.1)
2. Latest timestamp (nếu dùng format cũ)
3. Latest file modification time
Hot Reload/Unload
Reload Plugin Không Restart App
# Bước 1: Build plugin mới
dotnet build -c Release
# Bước 2: Upload version mới
curl -X POST http://localhost:5000/api/plugin/upload \
-F "file=@bin/Release/net8.0/MyPlugin.dll"
# Bước 3: Reload
curl -X POST http://localhost:5000/api/plugin/reload/MyPlugin
⚠️ Lưu ý:
- Plugin cũ sẽ bị unload (dispose resources)
- AssemblyLoadContext mới được tạo
- Tất cả commands được re-register
- Active requests vẫn chạy với code cũ (safe)
Reload All Plugins
POST /api/plugin/reload-all
Behavior:
- Reload tất cả loaded plugins
- Auto-load các plugin chưa load trong
Plugins/folder - Chọn version mới nhất cho mỗi plugin
🌐 API Endpoints
Plugin Management
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/plugin/upload |
Upload DLL/ZIP với version check | ✅ Root |
| POST | /api/plugin/load |
Load plugin từ path | ✅ Root |
| POST | /api/plugin/unload/{name} |
Unload plugin | ✅ Root |
| POST | /api/plugin/reload/{name} |
Reload plugin | ✅ Root |
| POST | /api/plugin/reload-all |
Reload all plugins | ✅ Root |
| DELETE | /api/plugin/remove/{name} |
Xóa plugin folder | ✅ Root |
| GET | /api/plugin/folders |
List plugin folders | ❌ Public |
| GET | /api/plugin/list |
List loaded plugins | ❌ Public |
| GET | /api/plugin/commands |
Get all commands | ❌ Public |
| GET | /api/plugin/commands/{name}/schema |
Get command schema | ❌ Public |
Plugin Execution
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/plugin/execute |
Execute command | ❌ Public (command-level auth) |
Developer Tools
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/plugin/sdk/download |
Download SDK ZIP (DLL + docs) |
| GET | /api/plugin/sdk/info |
Get SDK version info |
| GET | /api/plugin/template/download |
Download sample plugin template |
| GET | /api/plugin/services |
Discover available DI services |
| GET | /api/plugin/services/{name} |
Get service detail với code examples |
🐛 Debugging Plugins
Debug Mode (Project Reference)
Xem chi tiết tại: DiscoveryHostApi/README.md
Quick Start
- Add project reference trong
DiscoveryHostApi.csproj:
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<ProjectReference Include="..\MyPlugin\MyPlugin.csproj" />
</ItemGroup>
- Build plugin:
dotnet build -c Debug
- Run DiscoveryHostApi với debugger:
cd DiscoveryHostApi
dotnet run
Set breakpoints trong plugin code → F5 trong Visual Studio
Execute command qua Swagger UI → Breakpoint hit! ✅
📚 Ví Dụ Mẫu
DemoCalculator Plugin
# Download template
curl -O http://localhost:5000/api/plugin/template/download
unzip DemoPluginTemplate.zip
# Explore code
cd DemoCalculator
cat Commands/AddCommand.cs
cat plugin.json
# Build
dotnet build -c Release
# Upload
cd bin/Release/net8.0
curl -X POST http://localhost:5000/api/plugin/upload \
-F "file=@DemoCalculator.dll"
# Load
curl -X POST http://localhost:5000/api/plugin/load \
-H "Content-Type: application/json" \
-d '{"pluginPath": "DemoCalculator"}'
# Execute
curl -X POST http://localhost:5000/api/plugin/execute \
-H "Content-Type: application/json" \
-d '{
"command": "DemoCalculator.add",
"payload": {"a": 10, "b": 5}
}'
Các Commands Mẫu
| Command | Description | Example Payload |
|---|---|---|
add |
Cộng hai số | {"a": 10, "b": 5} |
subtract |
Trừ hai số | {"a": 10, "b": 3} |
multiply |
Nhân hai số | {"a": 4, "b": 5} |
divide |
Chia hai số | {"a": 20, "b": 4} |
power |
Lũy thừa | {"base": 2, "exponent": 3} |
sqrt |
Căn bậc hai | {"value": 16} |
factorial |
Giai thừa | {"n": 5} |
🤝 Đóng Góp
- Fork repository
- Create feature branch:
git checkout -b feature/my-feature - Commit changes:
git commit -am 'Add my feature' - Push to branch:
git push origin feature/my-feature - Submit Pull Request
📄 License
Proprietary License - Copyright © 2025 Thần Nông
Sử dụng thư viện này yêu cầu giấy phép từ Thần Nông.
📞 Support
- GitHub Issues: https://github.com/dungnt118/headless-core-backend-v3/issues
- Documentation:
DiscoveryHostApi/README.md - Email: support@thannong.com
🔗 Tài Liệu Liên Quan
- Plugin Development Guide:
Docs/PLUGIN_DEVELOPMENT.md - Schema Auto-Generation:
Docs/SCHEMA_AUTO_GENERATION.md - Authorization Quick Example:
Docs/AUTHORIZATION_QUICK_EXAMPLE.md - DiscoveryHostApi README: DiscoveryHostApi/README.md
Happy Plugin Development! 🚀
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Headless.SharedServices (>= 1.0.0)
- JsonLogic.Net (>= 1.1.11)
- McMaster.NETCore.Plugins (>= 2.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1 | 430 | 12/8/2025 |
Version 1.0.1:
- Plugin lifecycle management (Load/Unload/Reload)
- JsonLogic-based authorization
- Auto schema generation from method signatures
- Version-based DLL naming support
- Hot reload without app restart
- Isolated AssemblyLoadContext per plugin
- Debug mode with breakpoint support