Skip to main content

ASP.NET Core Packages

The Muonroi ASP.NET Core packages provide production-ready middleware, controllers, filters, and OpenAPI integration for building secure, multi-tenant APIs with permission-based authorization, license enforcement, and comprehensive exception handling.

Muonroi.AspNetCore

NuGet: Muonroi.AspNetCore | Tier: OSS | Distribution: NuGet.org

Purpose

Foundation package providing ASP.NET Core middleware, controller base classes, action filters, and DI registration for building secure APIs with permission-based authorization, license enforcement, quota tracking, and standardized error handling.

Key Types

TypeKindPurpose
MControllerBaseAbstract ClassBase controller with IMediator and logging support; routes: api/v{version}/[controller]
MAuthControllerBase<TPermission, TDbContext>Abstract ClassPre-built authentication/authorization endpoints for login, token refresh, roles, permissions, UI manifest generation
AuthorizePermissionFilter<TDbContext>Action FilterValidates endpoint permissions; supports multi-tenant tenant validation and policy decision services (PDP)
PermissionFilter<TPermission>Action FilterPermission bitmask evaluation from JWT claims; supports Any/All matching strategies
AuthorizePermissionAttributeAttributeMarks endpoints requiring specific permission keys; supports multiple attributes per endpoint
PermissionAttribute<TPermission>AttributeDecorates endpoints with enum-based permission requirements
LicenseMiddlewareMiddlewareEnforces license validity; records action usage; computes request hash for audit
MExceptionMiddlewareMiddlewareCatches unhandled exceptions; standardizes error responses; logs with correlation ID
MCookieAuthMiddlewareMiddlewareExtracts JWT from cookies; supports fallback to Authorization header
JwtMiddlewareMiddlewareJWT validation and extraction from Authorization header
QuotaEnforcementMiddlewareMiddlewareChecks tenant quota limits; blocks requests exceeding quotas
PermissionService<TPermission, TDbContext>ServiceRBAC management: roles, permissions, role-permission assignments, user management
PermissionQueryService<TDbContext>ServiceQuery helpers for roles, permissions, and role-permission mappings
UiEngineManifestOrchestrator<TDbContext>ServiceOrchestrates UI engine manifest generation from component registry, screens, actions
UiManifestBuilderServiceBuilds MUiManifest with navigation groups, screens, and actions
MDefaultControllerExecutionContextResolverServiceResolves execution context from HTTP request (user, tenant, language)

DI Registration

// Basic API setup with versioning, Swagger, health checks
services.AddBaseApi();

// Full infrastructure with permission, licensing, and tenancy
services.AddInfrastructure(configuration, assembly);

// Cookie-based authentication
services.AddMCookieAuth(configuration);

// JWT middleware
services.AddJwtMiddleware(configuration);

// Permission-based authorization
services.AddAuthorizePermission<MyPermission, MyDbContext>();

// Application layer: mediator, mappers, HTTP context accessor
services.AddApplication(assembly);

// Swagger with security definitions and error documentation
services.SwaggerConfig("API Name");

Usage Example

Define Permission-Protected Endpoint:

[ApiController]
[Route("api/v{version:apiVersion}/orders")]
public class OrderController : MControllerBase
{
private readonly IMediator _mediator;

public OrderController(IMediator mediator, IMLog<OrderController> logger)
: base(mediator, logger) { }

[HttpPost("create")]
[AuthorizePermission("orders.create", PermissionMatchMode.All)]
public async Task<IActionResult> CreateOrder([FromBody] CreateOrderRequest request)
{
var result = await Mediator.Send(request);
return Ok(result);
}

[HttpGet("list")]
[AuthorizePermission("orders.read", PermissionMatchMode.All)]
public async Task<IActionResult> ListOrders([FromQuery] int page = 1)
{
var result = await Mediator.Send(new ListOrdersQuery { Page = page });
return Ok(result);
}
}

Configure in Program.cs:

var builder = WebApplicationBuilder.CreateBuilder(args);
var configuration = builder.Configuration;
var assembly = typeof(Program).Assembly;

// Register services
builder.Services
.AddBaseApi()
.AddInfrastructure(configuration, assembly)
.AddApplication(assembly)
.SwaggerConfig("Order API");

var app = builder.Build();

// Enable middleware
app
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseLicense()
.UseQuotaEnforcement()
.MapControllers();

app.Run();

Inherit from Specialized Base Classes:

public class AuthController : MAuthControllerBase<PermissionEnum, AppDbContext>
{
public AuthController(
IAuthService<PermissionEnum, AppDbContext> authService,
IPermissionService<PermissionEnum> permissionService)
: base(authService, permissionService)
{
}

// Pre-built endpoints:
// POST /api/v1/auth/login
// POST /api/v1/auth/refresh-token
// POST /api/v1/auth/register
// POST /api/v1/auth/logout
// POST /api/v1/auth/create-role
// GET /api/v1/auth/user-permissions/{userId}
// GET /api/v1/auth/ui-engine/current
}

Muonroi.AspNetCore.OpenApi

NuGet: Muonroi.AspNetCore.OpenApi | Tier: OSS | Distribution: NuGet.org

Purpose

OpenAPI/Swagger integration providing automatic error response documentation, schema generation, and standardized API documentation through Swashbuckle filters.

Key Types

TypeKindPurpose
MErrorResponseFilterOperation FilterAuto-generates 400/500 error response documentation; adds MErrorResponse schema to all endpoints
SwaggerDefaultValuesOperation FilterNormalizes Swagger operation parameters and response types; removes unused content types

DI Registration

services.SwaggerConfig("API Name");

// In Program.cs after services are configured:
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

Usage Example

Automatic Error Documentation:

All endpoints automatically generate:

  • 400 Bad Request — validation or domain logic errors
  • 500 Internal Server Error — unhandled exceptions

Response schema for both HTTP 400 and 500:

{
"isSuccess": false,
"result": null,
"errors": [
{
"code": "VALIDATION_ERROR",
"message": "Field 'email' is required"
}
],
"timestamp": "2026-05-20T10:30:00Z"
}

Parameter Default Values:

SwaggerDefaultValues automatically:

  • Sets parameter descriptions from model metadata
  • Populates default values from query parameters
  • Marks required parameters
  • Filters content types to match declared response types

Muonroi.AspNetCore.RuleEngine

NuGet: Muonroi.AspNetCore.RuleEngine | Tier: OSS | Distribution: NuGet.org

Purpose

ASP.NET Core integration for the rule engine providing generic CRUD controllers for entities, rule change tracking, and UI engine change notifications.

Key Types

TypeKindPurpose
MGenericController<TEntity, TDbContext>ControllerProvides standard CRUD HTTP endpoints (GET, POST, PUT, DELETE) for any MEntity subclass; enforces permissions, tenancy, and license guards
GenericControllerFeatureProviderFeature ProviderDynamically registers MGenericController<T> instances for all MEntity subclasses discovered via reflection
GenericControllerRouteConventionRoute ConventionMaps generic controller routes to api/v{version}/[entity-plural] using automatic plural naming
CrudRuleExtensionsExtension MethodsHelper methods for CRUD permission checks and entity filtering
IRuleChangeStoreInterfacePersistent store for rule change history; supports approval workflows
InMemoryRuleChangeStoreImplementationVolatile in-memory store for rule changes
IRuleChangeProposalStoreInterfaceTracks rule change proposals and their states
InMemoryRuleChangeProposalStoreImplementationVolatile in-memory proposal storage
UiEngineChangesControllerControllerBroadcasts rule/entity changes to connected UI Engine clients via change notifications

DI Registration

// Registers generic controllers, rule change stores, and infrastructure
services.AddRuleEngineInfrastructure(configuration, typeof(Program).Assembly);

// Alternative: explicit registration
services.AddRuleEngineStore(configuration);
services.AddSingleton<IRuleChangeStore, InMemoryRuleChangeStore>();
services.AddSingleton<IRuleChangeProposalStore, InMemoryRuleChangeProposalStore>();
services.AddControllers(options =>
{
options.Conventions.Add(new GenericControllerRouteConvention());
})
.ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(new GenericControllerFeatureProvider(typeof(Program).Assembly));
});

Usage Example

Automatic CRUD Endpoints:

For an entity:

public class Product : MEntity
{
public string Name { get; set; }
public decimal Price { get; set; }
public bool IsActive { get; set; }
}

Automatically generates endpoints:

  • GET /api/v1/products?pageIndex=1&pageSize=10 — List with pagination
  • GET /api/v1/products/{id} — Get by ID
  • POST /api/v1/products — Create
  • PUT /api/v1/products/{id} — Update
  • DELETE /api/v1/products/{id} — Soft delete

Request/Response Examples:

# Create
POST /api/v1/products HTTP/1.1
Content-Type: application/json

{
"name": "Widget",
"price": 9.99,
"isActive": true
}

# Response
HTTP/1.1 201 Created
{
"isSuccess": true,
"result": {
"entityId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Widget",
"price": 9.99,
"isActive": true,
"creationTime": "2026-05-20T10:30:00Z"
}
}

Custom Rules in Generic Controller:

public class ProductController : MGenericController<Product, AppDbContext>
{
[HttpPost("bulk-import")]
[AuthorizePermission("products.import")]
public async Task<IActionResult> BulkImport([FromBody] List<CreateProductRequest> requests)
{
// Custom business logic
return Ok();
}
}

Rule Change Propagation:

When a rule or entity changes:

public class ProductService
{
private readonly IRuleChangeStore _changeStore;

public async Task UpdateProduct(Guid productId, UpdateProductRequest request)
{
var product = await _dbContext.Products.FindAsync(productId);
product.Price = request.Price;

await _dbContext.SaveChangesAsync();

// Record change for UI Engine
await _changeStore.RecordChangeAsync(new RuleChangeRecord
{
EntityName = "Product",
EntityId = productId,
ChangeType = "Update",
ChangedProperties = new[] { "Price" }
});
}
}

Common Workflows

Multi-Tenant Authorization Flow

  1. Request arrivesTenantResolutionMiddleware sets TenantContext.CurrentTenantId
  2. AuthenticationJwtMiddleware extracts JWT and validates claims
  3. License checkLicenseMiddleware validates license and records action
  4. Quota enforcementQuotaEnforcementMiddleware checks tenant quotas
  5. Permission validationAuthorizePermissionFilter evaluates endpoint requirements
  6. Authorization decision:
    • If IMPolicyDecisionService is enabled: delegates to policy engine
    • Otherwise: falls back to local RBAC from PermissionService
  7. Endpoint execution → Controller handles request with resolved tenant context

Permission Matching Strategies

All Mode (Default):

[AuthorizePermission("orders.read", PermissionMatchMode.All)]
[AuthorizePermission("orders.export", PermissionMatchMode.All)]
// User must have BOTH permissions

Any Mode:

[AuthorizePermission("admin.access", PermissionMatchMode.Any)]
[AuthorizePermission("super.access", PermissionMatchMode.Any)]
// User must have AT LEAST ONE permission

Mixed:

[AuthorizePermission("orders.read", PermissionMatchMode.All)]     // Required
[AuthorizePermission("export.pdf", PermissionMatchMode.Any)] // OR
[AuthorizePermission("export.excel", PermissionMatchMode.Any)] // OR
// User must have orders.read AND (export.pdf OR export.excel)

Exception Handling Pipeline

  1. Controller action throwsMExceptionMiddleware catches
  2. Exception classification → Determines error category and code
  3. Response building → Creates MErrorResponse with:
    • HTTP status code (400/500)
    • Error code and message
    • Correlation ID for tracking
    • Tenant context for multi-tenant logging
  4. Logging → Logs with structured scope (layer, tenant, user)
  5. Response sent → JSON response with Content-Type: application/json

Generic Controller Permission Checks

public class MGenericController<TEntity, TDbContext>
{
[HttpGet]
public virtual async Task<IActionResult> Get([FromQuery] int pageIndex = 1)
{
licenseGuard.EnsureValid("api.list", typeof(TEntity).Name);

if (!await CheckPermissionAsync("View")) // "View" permission required
{
return Forbid();
}

// List returns only records visible to current tenant
var items = ApplyTenantFilter(dbContext.Set<TEntity>());

return Ok(items);
}
}

Configuration Examples

appsettings.json

{
"ApiVersioning": {
"DefaultApiVersion": "1.0"
},
"Authentication": {
"Jwt": {
"Authority": "https://auth.example.com",
"Audience": "api"
},
"Cookie": {
"Name": "auth_token",
"SameSite": "Strict"
}
},
"License": {
"EnforceOnMiddleware": true,
"FilePath": "licenses/license.lic"
},
"MultiTenantConfigs": {
"Enabled": true,
"RequireTenantClaimForAuthenticatedUser": true
},
"Quota": {
"DefaultLimit": 1000
}
}

Swagger Security Definition

Automatically configured by SwaggerConfig():

services.SwaggerConfig("Order API");

// Generates:
// - Bearer token definition (JWT)
// - Global security requirement
// - Schemas for MErrorResponse (400, 500)
// - Operation filters for standardization

Best Practices

1. Always Inherit from Base Controllers

// Good: Gets mediator, logging, and version routing
public class ProductController : MControllerBase { }

// Or for auth endpoints:
public class AuthController : MAuthControllerBase<PermissionEnum, DbContext> { }

2. Use Permission Attributes Consistently

// Mark all endpoints with permissions
[HttpGet("{id}")]
[AuthorizePermission("products.read")]
public async Task<IActionResult> GetProduct(Guid id) { }

// Multiple permissions for complex scenarios
[HttpPost("export")]
[AuthorizePermission("products.read", PermissionMatchMode.All)]
[AuthorizePermission("export.pdf", PermissionMatchMode.Any)]
public async Task<IActionResult> ExportProducts() { }

3. Rely on Middleware for Cross-Cutting Concerns

app.UseRouting();
app.UseAuthentication(); // JWT/Cookie extraction
app.UseAuthorization(); // ASP.NET authorization
app.UseLicense(); // License validation
app.UseQuotaEnforcement(); // Quota checks
app.UseExceptionHandler(); // Global exception handling
app.MapControllers();

4. Use Generic Controllers for Standard CRUD

// Minimal code: register entities and get full CRUD
services.AddRuleEngineInfrastructure(configuration, assembly);

// Automatically get: GET, POST, PUT, DELETE
// With: permission checks, tenancy filters, license guards

5. Extend UI Manifest for Dynamic UI Generation

var manifest = await permissionService.GetUiEngineManifestAsync(userId);

// Returns screens, actions, navigation, components, rule bindings
// Can be projected with ?minimalFor=routing for route-only response