docs consolidation work
This commit is contained in:
@@ -781,6 +781,189 @@ endpoints:
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. Endpoints Not Discovered
|
||||
|
||||
**Symptom:** Gateway shows 0 endpoints for service, or specific endpoints missing.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| `UseStellaRouterBridge()` called before `MapControllers()` | Call `UseStellaRouterBridge()` after all endpoint registration |
|
||||
| Route filtered by `EndpointFilter` | Check filter logic, ensure endpoint matches |
|
||||
| Route filtered by `IncludePathPatterns` | Verify path pattern includes the endpoint |
|
||||
| Route filtered by `ExcludePathPatterns` | Verify endpoint isn't accidentally excluded |
|
||||
| Missing `[Authorize]` with `RequireExplicit` | Add authorization or change `MissingAuthorizationBehavior` |
|
||||
|
||||
**Debug:**
|
||||
```csharp
|
||||
// Enable discovery logging
|
||||
builder.Logging.AddFilter("StellaOps.Microservice.AspNetCore", LogLevel.Debug);
|
||||
```
|
||||
|
||||
#### 2. Authorization Claims Not Extracted
|
||||
|
||||
**Symptom:** Endpoints registered but `RequiringClaims` is empty when it shouldn't be.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| `[Authorize]` without policy/roles | Add explicit policy or roles |
|
||||
| Policy not registered | Register policy with `AddAuthorization()` |
|
||||
| `AuthorizationMappingStrategy.YamlOnly` set | Use `Hybrid` or `AspNetMetadataOnly` |
|
||||
| Custom policy doesn't have claim requirements | Use claims-based policies |
|
||||
|
||||
**Debug:**
|
||||
```csharp
|
||||
// Log authorization mapping
|
||||
var mapper = app.Services.GetRequiredService<IAuthorizationClaimMapper>();
|
||||
var result = await mapper.MapAsync(endpoint);
|
||||
Console.WriteLine($"Claims: {string.Join(", ", result.Claims)}");
|
||||
```
|
||||
|
||||
#### 3. Request Dispatch Fails with 404
|
||||
|
||||
**Symptom:** Gateway routes request but microservice returns 404.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| Path parameters not matched | Verify parameter names in route pattern |
|
||||
| Method mismatch | Verify HTTP method matches endpoint |
|
||||
| Route constraint rejected value | Use standard constraints that bridge supports |
|
||||
| Catch-all route not handled | Ensure `{**path}` is normalized correctly |
|
||||
|
||||
**Debug:**
|
||||
```bash
|
||||
# Check registered endpoints
|
||||
curl http://localhost:5000/.well-known/stella-endpoints
|
||||
```
|
||||
|
||||
#### 4. Model Binding Errors
|
||||
|
||||
**Symptom:** Requests return 400 Bad Request with binding errors.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| `[FromBody]` type mismatch | Verify request body matches expected type |
|
||||
| Required parameter missing | Include all `[FromRoute]`/`[FromQuery]` parameters |
|
||||
| `[FromHeader]` not populated | Headers forwarded via `X-StellaOps-Header-*` |
|
||||
| Complex type not deserialized | Verify JSON serialization settings |
|
||||
|
||||
**Debug:**
|
||||
```csharp
|
||||
// Enable model binding logging
|
||||
builder.Logging.AddFilter("Microsoft.AspNetCore.Mvc.ModelBinding", LogLevel.Debug);
|
||||
```
|
||||
|
||||
#### 5. Identity Not Populated
|
||||
|
||||
**Symptom:** `User.Identity` is null or claims missing in endpoint handler.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| Gateway not forwarding identity | Verify Gateway `identity-header-policy` configured |
|
||||
| Missing `X-StellaOps-UserId` header | Gateway must send identity headers |
|
||||
| `UseAuthentication()` not called | Call before `UseStellaRouterBridge()` |
|
||||
| Custom claims not mapped | Use `ClaimsPrincipalBuilder` for custom claims |
|
||||
|
||||
**Debug:**
|
||||
```csharp
|
||||
app.MapGet("/debug/identity", (HttpContext ctx) =>
|
||||
new {
|
||||
IsAuthenticated = ctx.User.Identity?.IsAuthenticated,
|
||||
Name = ctx.User.Identity?.Name,
|
||||
Claims = ctx.User.Claims.Select(c => new { c.Type, c.Value })
|
||||
});
|
||||
```
|
||||
|
||||
#### 6. Performance Issues
|
||||
|
||||
**Symptom:** High latency or memory usage.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| HttpContext allocation per request | Pool contexts (internal implementation) |
|
||||
| Large request bodies buffered | Use streaming endpoints for large payloads |
|
||||
| Discovery runs too frequently | Discovery runs once at startup; cache is stable |
|
||||
| Many endpoints slow startup | Discovery is O(n) but runs once |
|
||||
|
||||
**Metrics to monitor:**
|
||||
- `stellaops_router_bridge_requests_total`
|
||||
- `stellaops_router_bridge_request_duration_seconds`
|
||||
- `stellaops_router_bridge_dispatch_errors_total`
|
||||
|
||||
#### 7. YAML Override Not Applied
|
||||
|
||||
**Symptom:** Claims from YAML file not appearing in endpoint registration.
|
||||
|
||||
**Causes & Solutions:**
|
||||
|
||||
| Cause | Solution |
|
||||
|-------|----------|
|
||||
| `YamlConfigPath` not set | Set `options.YamlConfigPath = "router.yaml"` |
|
||||
| File not found | Use absolute path or verify relative path |
|
||||
| Path pattern doesn't match | YAML paths are case-insensitive, verify pattern |
|
||||
| `AuthorizationMappingStrategy.AspNetMetadataOnly` | Use `YamlOnly` or `Hybrid` |
|
||||
|
||||
**Example YAML:**
|
||||
```yaml
|
||||
# router.yaml
|
||||
endpoints:
|
||||
- path: "/api/admin/**"
|
||||
requiringClaims:
|
||||
- type: "Role"
|
||||
value: "admin"
|
||||
```
|
||||
|
||||
### Diagnostic Endpoints
|
||||
|
||||
The bridge adds optional diagnostic endpoints (development only):
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `/.well-known/stella-endpoints` | Lists all discovered endpoints |
|
||||
| `/.well-known/stella-bridge-status` | Shows bridge configuration and health |
|
||||
|
||||
Enable in development:
|
||||
```csharp
|
||||
builder.Services.AddStellaRouterBridge(options =>
|
||||
{
|
||||
options.EnableDiagnosticEndpoints = builder.Environment.IsDevelopment();
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Logging Categories
|
||||
|
||||
Configure logging for troubleshooting:
|
||||
|
||||
```json
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"StellaOps.Microservice.AspNetCore.Discovery": "Debug",
|
||||
"StellaOps.Microservice.AspNetCore.Authorization": "Debug",
|
||||
"StellaOps.Microservice.AspNetCore.Dispatch": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
@@ -96,6 +96,169 @@ Best for major refactoring or when HTTP compatibility is not needed.
|
||||
- More upfront work
|
||||
- Requires domain extraction
|
||||
|
||||
### Strategy C: ASP.NET Endpoint Bridge (Recommended)
|
||||
|
||||
Best for services that want automatic Router registration from existing ASP.NET endpoints without code changes.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ StellaOps.*.WebService │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ ASP.NET Endpoints (unchanged) ││
|
||||
│ │ • Minimal APIs: app.MapGet("/api/...", handler) ││
|
||||
│ │ • Controllers: [ApiController] with [HttpGet], etc. ││
|
||||
│ │ • Route groups: app.MapGroup("/api").MapEndpoints() ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
│ │ │
|
||||
│ ▼ Auto-discovery │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ StellaOps.Microservice.AspNetCore ││
|
||||
│ │ • Discovers all endpoints from EndpointDataSource ││
|
||||
│ │ • Extracts authorization metadata automatically ││
|
||||
│ │ • Registers with Router via HELLO ││
|
||||
│ │ • Dispatches Router requests through ASP.NET pipeline ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
│ │ │
|
||||
│ ▼ Binary transport │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ Router Gateway ││
|
||||
│ │ • Routes external HTTP → internal microservice ││
|
||||
│ │ • Aggregates OpenAPI ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Add `StellaOps.Microservice.AspNetCore` package reference
|
||||
2. Configure bridge in `Program.cs`:
|
||||
|
||||
```csharp
|
||||
using StellaOps.Microservice.AspNetCore;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add authorization (required for claim mapping)
|
||||
builder.Services.AddAuthorization();
|
||||
|
||||
// Add the ASP.NET Endpoint Bridge
|
||||
builder.Services.AddStellaRouterBridge(options =>
|
||||
{
|
||||
options.ServiceName = "my-service";
|
||||
options.Version = "1.0.0";
|
||||
options.Region = "us-east-1";
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Register ASP.NET endpoints as usual
|
||||
app.MapGet("/api/health", () => "healthy");
|
||||
app.MapGet("/api/items/{id}", (string id) => new { id, name = "Item " + id })
|
||||
.RequireAuthorization("read-items");
|
||||
app.MapPost("/api/items", (CreateItemRequest req) => new { id = Guid.NewGuid() })
|
||||
.RequireAuthorization("write-items");
|
||||
|
||||
// Activate the bridge (discovers and registers endpoints)
|
||||
app.UseStellaRouterBridge();
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
3. (Optional) Add YAML overrides for security hardening:
|
||||
|
||||
```yaml
|
||||
# router.yaml
|
||||
endpoints:
|
||||
- path: "/api/admin/**"
|
||||
requiringClaims:
|
||||
- type: "Role"
|
||||
value: "admin"
|
||||
timeoutMs: 60000
|
||||
```
|
||||
|
||||
4. Test via Gateway
|
||||
5. Deploy to production
|
||||
|
||||
**Pros:**
|
||||
- **Zero code changes** to existing endpoints
|
||||
- Automatic authorization metadata extraction
|
||||
- Full ASP.NET pipeline execution (filters, binding, validation)
|
||||
- Deterministic endpoint ordering
|
||||
- YAML overrides for security hardening without code changes
|
||||
- Supports both minimal APIs and controllers
|
||||
|
||||
**Cons:**
|
||||
- Slightly more runtime overhead (HttpContext synthesis)
|
||||
- Some ASP.NET features not supported (see limitations below)
|
||||
|
||||
#### Authorization Mapping
|
||||
|
||||
The bridge automatically extracts authorization requirements:
|
||||
|
||||
| ASP.NET Source | Router Mapping |
|
||||
|----------------|----------------|
|
||||
| `[Authorize(Policy = "policy-name")]` | Resolved via `IAuthorizationPolicyProvider` → `RequiringClaims` |
|
||||
| `[Authorize(Roles = "admin,user")]` | `ClaimRequirement(Role, admin)`, `ClaimRequirement(Role, user)` |
|
||||
| `.RequireAuthorization("policy")` | Resolved via policy provider |
|
||||
| `.RequireAuthorization(new AuthorizeAttribute { Roles = "..." })` | Direct role mapping |
|
||||
| `[AllowAnonymous]` | Empty `RequiringClaims` (public endpoint) |
|
||||
|
||||
#### Authorization Mapping Strategies
|
||||
|
||||
Configure how the bridge handles authorization:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddStellaRouterBridge(options =>
|
||||
{
|
||||
options.ServiceName = "my-service";
|
||||
options.Version = "1.0.0";
|
||||
options.Region = "us-east-1";
|
||||
|
||||
// Strategy options:
|
||||
// - YamlOnly: YAML claims replace code claims entirely
|
||||
// - AspNetMetadataOnly: Code claims only, ignore YAML (default)
|
||||
// - Hybrid: Merge code + YAML claims by type
|
||||
options.AuthorizationMappingStrategy = AuthorizationMappingStrategy.Hybrid;
|
||||
|
||||
// Behavior when endpoint has no authorization:
|
||||
// - RequireExplicit: Fail validation (secure default)
|
||||
// - AllowAuthenticated: Require authentication but no specific claims
|
||||
// - WarnAndAllow: Log warning, allow request (dev only)
|
||||
options.MissingAuthorizationBehavior = MissingAuthorizationBehavior.RequireExplicit;
|
||||
});
|
||||
```
|
||||
|
||||
#### Route Filtering
|
||||
|
||||
Control which endpoints are bridged:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddStellaRouterBridge(options =>
|
||||
{
|
||||
options.ServiceName = "my-service";
|
||||
options.Version = "1.0.0";
|
||||
options.Region = "us-east-1";
|
||||
|
||||
// Include only /api/* endpoints
|
||||
options.IncludePathPatterns = ["/api/**"];
|
||||
|
||||
// Exclude internal endpoints
|
||||
options.ExcludePathPatterns = ["/internal/**", "/metrics", "/health/**"];
|
||||
});
|
||||
```
|
||||
|
||||
#### Limitations
|
||||
|
||||
The ASP.NET Endpoint Bridge does **not** support:
|
||||
|
||||
| Feature | Alternative |
|
||||
|---------|-------------|
|
||||
| SignalR/WebSocket | Use direct HTTP for real-time features |
|
||||
| gRPC endpoints | Use separate gRPC channel |
|
||||
| Streaming request bodies | Use `IRawStellaEndpoint` for streaming |
|
||||
| Custom route constraints (`{id:guid}`) | Constraints execute at dispatch, but not in discovery |
|
||||
| Header/query-based API versioning | Use path-based versioning (`/api/v1/...`) |
|
||||
|
||||
## Controller to Handler Mapping
|
||||
|
||||
### Before (ASP.NET Controller)
|
||||
|
||||
Reference in New Issue
Block a user