yes
This commit is contained in:
parent
e19fad2baa
commit
2ee65b8dad
@ -31,7 +31,8 @@
|
|||||||
"Bash(grpcurl:*)",
|
"Bash(grpcurl:*)",
|
||||||
"Bash(lsof:*)",
|
"Bash(lsof:*)",
|
||||||
"Bash(xargs kill -9)",
|
"Bash(xargs kill -9)",
|
||||||
"Bash(dotnet run:*)"
|
"Bash(dotnet run:*)",
|
||||||
|
"Bash(find:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,6 +3,8 @@
|
|||||||
##
|
##
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
.research/
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
*.suo
|
*.suo
|
||||||
|
|||||||
199
CLAUDE.md
199
CLAUDE.md
@ -6,28 +6,33 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
This is Svrnty.CQRS, a modern implementation of Command Query Responsibility Segregation (CQRS) for .NET 10. It was forked from PoweredSoft.CQRS and provides:
|
This is Svrnty.CQRS, a modern implementation of Command Query Responsibility Segregation (CQRS) for .NET 10. It was forked from PoweredSoft.CQRS and provides:
|
||||||
|
|
||||||
- Automatic REST endpoint generation from command/query handlers
|
- CQRS pattern implementation with command/query handlers exposed via HTTP or gRPC
|
||||||
|
- Automatic HTTP endpoint generation via Minimal API
|
||||||
|
- Automatic gRPC endpoint generation with source generators and Google Rich Error Model validation
|
||||||
- Dynamic query capabilities (filtering, sorting, grouping, aggregation)
|
- Dynamic query capabilities (filtering, sorting, grouping, aggregation)
|
||||||
- ASP.NET Core MVC integration
|
- FluentValidation support with RFC 7807 Problem Details (HTTP) and Google Rich Error Model (gRPC)
|
||||||
- FluentValidation support
|
- AOT (Ahead-of-Time) compilation compatibility for core packages (where dependencies allow)
|
||||||
- AOT (Ahead-of-Time) compilation compatibility for core packages
|
|
||||||
|
|
||||||
## Solution Structure
|
## Solution Structure
|
||||||
|
|
||||||
The solution contains 9 projects organized by responsibility:
|
The solution contains 11 projects organized by responsibility (10 packages + 1 sample project):
|
||||||
|
|
||||||
**Abstractions (interfaces and contracts only):**
|
**Abstractions (interfaces and contracts only):**
|
||||||
- `Svrnty.CQRS.Abstractions` - Core interfaces (ICommandHandler, IQueryHandler, discovery contracts)
|
- `Svrnty.CQRS.Abstractions` - Core interfaces (ICommandHandler, IQueryHandler, discovery contracts)
|
||||||
- `Svrnty.CQRS.AspNetCore.Abstractions` - ASP.NET Core attributes
|
|
||||||
- `Svrnty.CQRS.DynamicQuery.Abstractions` - Dynamic query interfaces (multi-targets netstandard2.1 and net10.0)
|
- `Svrnty.CQRS.DynamicQuery.Abstractions` - Dynamic query interfaces (multi-targets netstandard2.1 and net10.0)
|
||||||
|
- `Svrnty.CQRS.Grpc.Abstractions` - gRPC-specific interfaces and contracts
|
||||||
|
|
||||||
**Implementation:**
|
**Implementation:**
|
||||||
- `Svrnty.CQRS` - Core discovery and registration logic
|
- `Svrnty.CQRS` - Core discovery and registration logic
|
||||||
- `Svrnty.CQRS.AspNetCore` - MVC controller generation (legacy/backward compatibility)
|
- `Svrnty.CQRS.MinimalApi` - Minimal API endpoint mapping for commands/queries (recommended for HTTP)
|
||||||
- `Svrnty.CQRS.MinimalApi` - Minimal API endpoint mapping (recommended for new projects)
|
- `Svrnty.CQRS.DynamicQuery` - PoweredSoft.DynamicQuery integration for advanced filtering
|
||||||
- `Svrnty.CQRS.DynamicQuery` - PoweredSoft.DynamicQuery integration
|
- `Svrnty.CQRS.DynamicQuery.MinimalApi` - Minimal API endpoint mapping for dynamic queries
|
||||||
- `Svrnty.CQRS.DynamicQuery.AspNetCore` - Dynamic query controllers
|
|
||||||
- `Svrnty.CQRS.FluentValidation` - Validation integration helpers
|
- `Svrnty.CQRS.FluentValidation` - Validation integration helpers
|
||||||
|
- `Svrnty.CQRS.Grpc` - gRPC service implementation support
|
||||||
|
- `Svrnty.CQRS.Grpc.Generators` - Source generator for .proto files and gRPC service implementations
|
||||||
|
|
||||||
|
**Sample Projects:**
|
||||||
|
- `Svrnty.Sample` - Comprehensive demo project showcasing both HTTP and gRPC endpoints
|
||||||
|
|
||||||
**Key Design Principle:** Abstractions projects contain ONLY interfaces/attributes with minimal dependencies. Implementation projects depend on abstractions. This allows consumers to reference abstractions without pulling in heavy implementation dependencies.
|
**Key Design Principle:** Abstractions projects contain ONLY interfaces/attributes with minimal dependencies. Implementation projects depend on abstractions. This allows consumers to reference abstractions without pulling in heavy implementation dependencies.
|
||||||
|
|
||||||
@ -89,24 +94,77 @@ The framework uses a **metadata pattern** for runtime discovery:
|
|||||||
- Query all registered metadata from DI container
|
- Query all registered metadata from DI container
|
||||||
- Provide lookup methods: `GetCommand(string name)`, `GetCommands()`, etc.
|
- Provide lookup methods: `GetCommand(string name)`, `GetCommands()`, etc.
|
||||||
|
|
||||||
3. ASP.NET Core feature providers use discovery to:
|
3. Endpoint mapping (HTTP and gRPC) uses discovery to:
|
||||||
- Enumerate all registered commands/queries
|
- Enumerate all registered commands/queries
|
||||||
- Dynamically generate generic controller instances at startup
|
- Dynamically generate endpoints at application startup
|
||||||
- Apply naming conventions (convert to lowerCamelCase)
|
- Apply naming conventions (convert to lowerCamelCase)
|
||||||
|
- Generate gRPC service implementations via source generators
|
||||||
|
|
||||||
**Key Files:**
|
**Key Files:**
|
||||||
- `Svrnty.CQRS.Abstractions/Discovery/` - Metadata interfaces
|
- `Svrnty.CQRS.Abstractions/Discovery/` - Metadata interfaces
|
||||||
- `Svrnty.CQRS/Discovery/` - Discovery implementations
|
- `Svrnty.CQRS/Discovery/` - Discovery implementations
|
||||||
- `Svrnty.CQRS.AspNetCore/Mvc/*FeatureProvider.cs` - Dynamic controller generation
|
- `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs` - HTTP endpoint generation
|
||||||
- `Svrnty.CQRS.AspNetCore/Mvc/*Convention.cs` - Naming conventions
|
- `Svrnty.CQRS.DynamicQuery.MinimalApi/EndpointRouteBuilderExtensions.cs` - Dynamic query endpoint generation
|
||||||
|
- `Svrnty.CQRS.Grpc.Generators/` - gRPC service generation via source generators
|
||||||
|
|
||||||
### ASP.NET Core Integration Options
|
### Integration Options
|
||||||
|
|
||||||
There are two options for integrating commands and queries with ASP.NET Core:
|
There are two primary integration options for exposing commands and queries:
|
||||||
|
|
||||||
#### Option 1: Minimal API (Recommended)
|
#### Option 1: gRPC (Recommended for performance-critical scenarios)
|
||||||
|
|
||||||
The **Svrnty.CQRS.MinimalApi** package provides modern Minimal API endpoints:
|
The **Svrnty.CQRS.Grpc** package with **Svrnty.CQRS.Grpc.Generators** source generator provides high-performance gRPC endpoints:
|
||||||
|
|
||||||
|
**Registration:**
|
||||||
|
```csharp
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Register CQRS services
|
||||||
|
builder.Services.AddSvrntyCQRS();
|
||||||
|
builder.Services.AddDefaultCommandDiscovery();
|
||||||
|
builder.Services.AddDefaultQueryDiscovery();
|
||||||
|
|
||||||
|
// Add your commands and queries
|
||||||
|
builder.Services.AddCommand<AddUserCommand, int, AddUserCommandHandler>();
|
||||||
|
builder.Services.AddCommand<RemoveUserCommand, RemoveUserCommandHandler>();
|
||||||
|
|
||||||
|
// Add gRPC support
|
||||||
|
builder.Services.AddGrpc();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Map auto-generated gRPC service implementations
|
||||||
|
app.MapGrpcService<CommandServiceImpl>();
|
||||||
|
app.MapGrpcService<QueryServiceImpl>();
|
||||||
|
|
||||||
|
// Enable gRPC reflection for tools like grpcurl
|
||||||
|
app.MapGrpcReflectionService();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
```
|
||||||
|
|
||||||
|
**How It Works:**
|
||||||
|
1. Define `.proto` files in `Protos/` directory with your commands/queries as messages
|
||||||
|
2. Source generator automatically creates `CommandServiceImpl` and `QueryServiceImpl` implementations
|
||||||
|
3. Property names in C# commands must match proto field names (case-insensitive)
|
||||||
|
4. FluentValidation is automatically integrated with **Google Rich Error Model** for structured validation errors
|
||||||
|
5. Validation errors return `google.rpc.Status` with `BadRequest` containing `FieldViolations`
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- High-performance binary protocol
|
||||||
|
- Automatic service implementation generation at compile time
|
||||||
|
- Google Rich Error Model for structured validation errors
|
||||||
|
- Full FluentValidation integration
|
||||||
|
- gRPC reflection support for development tools
|
||||||
|
- Suitable for microservices, internal APIs, and low-latency scenarios
|
||||||
|
|
||||||
|
**Key Files:**
|
||||||
|
- `Svrnty.CQRS.Grpc/` - Runtime support for gRPC services
|
||||||
|
- `Svrnty.CQRS.Grpc.Generators/` - Source generator for service implementations
|
||||||
|
|
||||||
|
#### Option 2: HTTP via Minimal API (Recommended for web/browser scenarios)
|
||||||
|
|
||||||
|
The **Svrnty.CQRS.MinimalApi** package provides HTTP endpoints for CQRS commands and queries:
|
||||||
|
|
||||||
**Registration:**
|
**Registration:**
|
||||||
```csharp
|
```csharp
|
||||||
@ -121,6 +179,10 @@ builder.Services.AddDefaultQueryDiscovery();
|
|||||||
builder.Services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
|
builder.Services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
|
||||||
builder.Services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
|
builder.Services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
|
||||||
|
|
||||||
|
// Add Swagger (optional)
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Map endpoints (this creates routes automatically)
|
// Map endpoints (this creates routes automatically)
|
||||||
@ -133,7 +195,7 @@ app.Run();
|
|||||||
**How It Works:**
|
**How It Works:**
|
||||||
1. Extension methods iterate through `ICommandDiscovery` and `IQueryDiscovery`
|
1. Extension methods iterate through `ICommandDiscovery` and `IQueryDiscovery`
|
||||||
2. For each command/query, creates Minimal API endpoints using `MapPost()`/`MapGet()`
|
2. For each command/query, creates Minimal API endpoints using `MapPost()`/`MapGet()`
|
||||||
3. Applies same naming conventions as MVC (lowerCamelCase)
|
3. Applies naming conventions (lowerCamelCase)
|
||||||
4. Respects `[CommandControllerIgnore]` and `[QueryControllerIgnore]` attributes
|
4. Respects `[CommandControllerIgnore]` and `[QueryControllerIgnore]` attributes
|
||||||
5. Integrates with `ICommandAuthorizationService` and `IQueryAuthorizationService`
|
5. Integrates with `ICommandAuthorizationService` and `IQueryAuthorizationService`
|
||||||
6. Supports OpenAPI/Swagger documentation
|
6. Supports OpenAPI/Swagger documentation
|
||||||
@ -144,37 +206,53 @@ app.Run();
|
|||||||
- Authorization via authorization services (returns 401/403 status codes)
|
- Authorization via authorization services (returns 401/403 status codes)
|
||||||
- Customizable route prefixes: `MapSvrntyCommands("my-prefix")`
|
- Customizable route prefixes: `MapSvrntyCommands("my-prefix")`
|
||||||
- Automatic OpenAPI tags: "Commands" and "Queries"
|
- Automatic OpenAPI tags: "Commands" and "Queries"
|
||||||
|
- RFC 7807 Problem Details for validation errors
|
||||||
|
- Full Swagger/OpenAPI support
|
||||||
|
|
||||||
**Key Files:**
|
**Key Files:**
|
||||||
- `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs` - Main implementation
|
- `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs` - Main implementation
|
||||||
|
|
||||||
#### Option 2: MVC Controllers (Legacy)
|
#### Option 3: Both gRPC and HTTP (Dual Protocol Support)
|
||||||
|
|
||||||
The AspNetCore packages use **dynamic controller generation** (maintained for backward compatibility):
|
You can enable both protocols simultaneously, allowing clients to choose their preferred protocol:
|
||||||
|
|
||||||
1. **Feature Providers** (`CommandControllerFeatureProvider`, `QueryControllerFeatureProvider`):
|
|
||||||
- Called during MVC startup
|
|
||||||
- Discover handlers via `ICommandDiscovery`/`IQueryDiscovery`
|
|
||||||
- Create generic controller types: `CommandController<TCommand>`, `QueryController<TQuery, TResult>`
|
|
||||||
- Skip handlers marked with `[CommandControllerIgnore]` or `[QueryControllerIgnore]`
|
|
||||||
|
|
||||||
2. **Conventions** (`CommandControllerConvention`, `QueryControllerConvention`):
|
|
||||||
- Apply naming to generated controllers
|
|
||||||
- Default: Type name without suffix, converted to lowerCamelCase
|
|
||||||
- Custom: Use `[CommandName]` or `[QueryName]` attribute
|
|
||||||
|
|
||||||
3. **Controllers** expose automatic endpoints:
|
|
||||||
- Commands: `POST /api/command/{name}`
|
|
||||||
- Queries: `POST /api/query/{name}` or `GET /api/query/{name}`
|
|
||||||
|
|
||||||
**Registration Pattern:**
|
|
||||||
```csharp
|
```csharp
|
||||||
services
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
.AddControllers()
|
|
||||||
.AddSvrntyQueries() // Registers query feature provider
|
// Register CQRS services
|
||||||
.AddSvrntyCommands(); // Registers command feature provider
|
builder.Services.AddSvrntyCQRS();
|
||||||
|
builder.Services.AddDefaultCommandDiscovery();
|
||||||
|
builder.Services.AddDefaultQueryDiscovery();
|
||||||
|
|
||||||
|
// Add commands and queries
|
||||||
|
AddCommands(builder.Services);
|
||||||
|
AddQueries(builder.Services);
|
||||||
|
|
||||||
|
// Add both gRPC and HTTP support
|
||||||
|
builder.Services.AddGrpc();
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Map both gRPC and HTTP endpoints
|
||||||
|
app.MapGrpcService<CommandServiceImpl>();
|
||||||
|
app.MapGrpcService<QueryServiceImpl>();
|
||||||
|
app.MapGrpcReflectionService();
|
||||||
|
|
||||||
|
app.MapSvrntyCommands();
|
||||||
|
app.MapSvrntyQueries();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Single codebase supports multiple protocols
|
||||||
|
- gRPC for high-performance, low-latency scenarios (microservices, internal APIs)
|
||||||
|
- HTTP for web browsers, legacy clients, and public APIs
|
||||||
|
- Same commands, queries, and validation logic for both protocols
|
||||||
|
- Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients
|
||||||
|
|
||||||
### Dynamic Query System
|
### Dynamic Query System
|
||||||
|
|
||||||
Dynamic queries provide OData-like filtering capabilities:
|
Dynamic queries provide OData-like filtering capabilities:
|
||||||
@ -187,7 +265,7 @@ Dynamic queries provide OData-like filtering capabilities:
|
|||||||
|
|
||||||
**Request Flow:**
|
**Request Flow:**
|
||||||
1. HTTP request with filters/sorts/aggregates
|
1. HTTP request with filters/sorts/aggregates
|
||||||
2. DynamicQueryController receives request
|
2. Minimal API endpoint receives request
|
||||||
3. DynamicQueryHandler gets base queryable from IQueryableProvider
|
3. DynamicQueryHandler gets base queryable from IQueryableProvider
|
||||||
4. Applies alterations from all registered IAlterQueryableService instances
|
4. Applies alterations from all registered IAlterQueryableService instances
|
||||||
5. Builds PoweredSoft query criteria
|
5. Builds PoweredSoft query criteria
|
||||||
@ -195,14 +273,18 @@ Dynamic queries provide OData-like filtering capabilities:
|
|||||||
|
|
||||||
**Registration Example:**
|
**Registration Example:**
|
||||||
```csharp
|
```csharp
|
||||||
|
// Register dynamic query
|
||||||
services.AddDynamicQuery<Person, PersonDto>()
|
services.AddDynamicQuery<Person, PersonDto>()
|
||||||
.AddDynamicQueryWithProvider<Person, PersonQueryableProvider>()
|
.AddDynamicQueryWithProvider<Person, PersonQueryableProvider>()
|
||||||
.AddAlterQueryable<Person, PersonDto, SecurityFilter>();
|
.AddAlterQueryable<Person, PersonDto, SecurityFilter>();
|
||||||
|
|
||||||
|
// Map dynamic query endpoints
|
||||||
|
app.MapSvrntyDynamicQueries(); // Creates POST/GET /api/query/{queryName} endpoints
|
||||||
```
|
```
|
||||||
|
|
||||||
**Key Files:**
|
**Key Files:**
|
||||||
- `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs`
|
- `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs` - Query execution logic
|
||||||
- `Svrnty.CQRS.DynamicQuery.AspNetCore/DynamicQuery.cs` - Request models
|
- `Svrnty.CQRS.DynamicQuery.MinimalApi/EndpointRouteBuilderExtensions.cs` - HTTP endpoint mapping
|
||||||
|
|
||||||
## Package Configuration
|
## Package Configuration
|
||||||
|
|
||||||
@ -214,16 +296,28 @@ All projects target .NET 10.0 and use C# 14, sharing common configuration:
|
|||||||
- **Symbols**: Portable debug symbols with source, published as `.snupkg`
|
- **Symbols**: Portable debug symbols with source, published as `.snupkg`
|
||||||
- **NuGet metadata**: Icon, README, license (MIT), and repository URL included in packages
|
- **NuGet metadata**: Icon, README, license (MIT), and repository URL included in packages
|
||||||
- **Authors**: David Lebee, Mathias Beaulieu-Duncan
|
- **Authors**: David Lebee, Mathias Beaulieu-Duncan
|
||||||
- **Repository**: https://git.openharbor.io/Open-Harbor/dotnet-cqrs
|
- **Repository**: https://git.openharbor.io/svrnty/dotnet-cqrs
|
||||||
|
|
||||||
### Package Dependencies
|
### Package Dependencies
|
||||||
|
|
||||||
|
**Core Dependencies:**
|
||||||
- **Microsoft.Extensions.DependencyInjection.Abstractions**: 10.0.0-rc.2.25502.107 (will update to stable when .NET 10 is released)
|
- **Microsoft.Extensions.DependencyInjection.Abstractions**: 10.0.0-rc.2.25502.107 (will update to stable when .NET 10 is released)
|
||||||
- **FluentValidation**: 11.11.0
|
- **FluentValidation**: 11.11.0
|
||||||
- **Microsoft.CodeAnalysis.CSharp**: 4.13.0
|
|
||||||
- **PoweredSoft.DynamicQuery**: 3.0.1
|
- **PoweredSoft.DynamicQuery**: 3.0.1
|
||||||
- **Pluralize.NET**: 1.0.2
|
- **Pluralize.NET**: 1.0.2
|
||||||
|
|
||||||
|
**gRPC Dependencies (for Svrnty.CQRS.Grpc):**
|
||||||
|
- **Grpc.AspNetCore**: 2.68.0 or later
|
||||||
|
- **Grpc.AspNetCore.Server.Reflection**: 2.71.0 or later (optional, for reflection)
|
||||||
|
- **Grpc.StatusProto**: 2.71.0 or later (for Rich Error Model validation)
|
||||||
|
- **Grpc.Tools**: 2.76.0 or later (for .proto compilation)
|
||||||
|
|
||||||
|
**Source Generator Dependencies (for Svrnty.CQRS.Grpc.Generators):**
|
||||||
|
- **Microsoft.CodeAnalysis.CSharp**: 5.0.0-2.final
|
||||||
|
- **Microsoft.CodeAnalysis.Analyzers**: 3.11.0
|
||||||
|
- **Microsoft.Build.Utilities.Core**: 17.0.0
|
||||||
|
- Targets: netstandard2.0 (for Roslyn compatibility)
|
||||||
|
|
||||||
## Publishing
|
## Publishing
|
||||||
|
|
||||||
NuGet packages are published automatically via GitHub Actions when a release is created:
|
NuGet packages are published automatically via GitHub Actions when a release is created:
|
||||||
@ -298,9 +392,12 @@ The codebase currently compiles without warnings on C# 14.
|
|||||||
|
|
||||||
4. **Metadata Pattern**: When extending discovery, always create corresponding metadata classes (implement ICommandMeta/IQueryMeta).
|
4. **Metadata Pattern**: When extending discovery, always create corresponding metadata classes (implement ICommandMeta/IQueryMeta).
|
||||||
|
|
||||||
5. **Feature Provider Timing**: Controller generation happens during MVC startup. Discovery services must be registered before calling AddSvrntyCommands/Queries.
|
5. **Endpoint Mapping Timing**: Endpoints are mapped at application startup. Discovery services must be registered before calling `MapSvrntyCommands()`/`MapSvrntyQueries()` or mapping gRPC services.
|
||||||
|
|
||||||
6. **FluentValidation**: This framework only REGISTERS validators. Actual validation execution requires separate middleware/filters in consumer applications.
|
6. **FluentValidation Integration**:
|
||||||
|
- For HTTP: Validation happens automatically in the Minimal API pipeline. Errors return RFC 7807 Problem Details.
|
||||||
|
- For gRPC: Validation happens automatically via source-generated services. Errors return Google Rich Error Model with structured FieldViolations.
|
||||||
|
- The framework REGISTERS validators in DI; actual validation execution is handled by the endpoint implementations.
|
||||||
|
|
||||||
7. **DynamicQuery Interceptors**: Support up to 5 interceptors per query type. Interceptors modify PoweredSoft DynamicQuery behavior.
|
7. **DynamicQuery Interceptors**: Support up to 5 interceptors per query type. Interceptors modify PoweredSoft DynamicQuery behavior.
|
||||||
|
|
||||||
@ -309,6 +406,8 @@ The codebase currently compiles without warnings on C# 14.
|
|||||||
- Handler interfaces: `Svrnty.CQRS.Abstractions/ICommandHandler.cs`, `IQueryHandler.cs`
|
- Handler interfaces: `Svrnty.CQRS.Abstractions/ICommandHandler.cs`, `IQueryHandler.cs`
|
||||||
- Discovery implementations: `Svrnty.CQRS/Discovery/`
|
- Discovery implementations: `Svrnty.CQRS/Discovery/`
|
||||||
- Service registration: `*/ServiceCollectionExtensions.cs` in each project
|
- Service registration: `*/ServiceCollectionExtensions.cs` in each project
|
||||||
- Controller templates: `Svrnty.CQRS.AspNetCore/Mvc/*Controller.cs`
|
- HTTP endpoint mapping: `Svrnty.CQRS.MinimalApi/EndpointRouteBuilderExtensions.cs`
|
||||||
- Dynamic query logic: `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs`
|
- Dynamic query logic: `Svrnty.CQRS.DynamicQuery/DynamicQueryHandler.cs`
|
||||||
- Naming/routing: `Svrnty.CQRS.AspNetCore/Mvc/*Convention.cs`
|
- Dynamic query endpoints: `Svrnty.CQRS.DynamicQuery.MinimalApi/EndpointRouteBuilderExtensions.cs`
|
||||||
|
- gRPC support: `Svrnty.CQRS.Grpc/` runtime, `Svrnty.CQRS.Grpc.Generators/` source generators
|
||||||
|
- Sample application: `Svrnty.Sample/` - demonstrates both HTTP and gRPC integration
|
||||||
|
|||||||
11
README.md
11
README.md
@ -24,7 +24,6 @@ Our implementation of query and command responsibility segregation (CQRS).
|
|||||||
| Package Name | NuGet | NuGet Install |
|
| Package Name | NuGet | NuGet Install |
|
||||||
| ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: |
|
| ---------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -----------------------------------------------------: |
|
||||||
| Svrnty.CQRS.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.Abstractions/) | ```dotnet add package Svrnty.CQRS.Abstractions ``` |
|
| Svrnty.CQRS.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.Abstractions/) | ```dotnet add package Svrnty.CQRS.Abstractions ``` |
|
||||||
| Svrnty.CQRS.AspNetCore.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.AspNetCore.Abstractions/) | ```dotnet add package Svrnty.CQRS.AspNetCore.Abstractions ``` |
|
|
||||||
| Svrnty.CQRS.DynamicQuery.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.Abstractions/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.Abstractions ``` |
|
| Svrnty.CQRS.DynamicQuery.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.DynamicQuery.Abstractions/) | ```dotnet add package Svrnty.CQRS.DynamicQuery.Abstractions ``` |
|
||||||
| Svrnty.CQRS.Grpc.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.Grpc.Abstractions/) | ```dotnet add package Svrnty.CQRS.Grpc.Abstractions ``` |
|
| Svrnty.CQRS.Grpc.Abstractions | [](https://www.nuget.org/packages/Svrnty.CQRS.Grpc.Abstractions/) | ```dotnet add package Svrnty.CQRS.Grpc.Abstractions ``` |
|
||||||
|
|
||||||
@ -124,9 +123,9 @@ public record RemoveUserCommand
|
|||||||
- Use `record` types for commands/queries (immutable, value-based equality, more concise)
|
- Use `record` types for commands/queries (immutable, value-based equality, more concise)
|
||||||
- No need for protobuf-net attributes
|
- No need for protobuf-net attributes
|
||||||
|
|
||||||
## Sample of startup code for Minimal API (Traditional HTTP)
|
## Sample of startup code for Minimal API (HTTP)
|
||||||
|
|
||||||
For traditional HTTP/REST scenarios, you can use the Minimal API approach:
|
For HTTP scenarios (web browsers, public APIs), you can use the Minimal API approach:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
@ -184,7 +183,7 @@ AddCommands(builder.Services);
|
|||||||
// Add gRPC support
|
// Add gRPC support
|
||||||
builder.Services.AddGrpc();
|
builder.Services.AddGrpc();
|
||||||
|
|
||||||
// Add HTTP/REST support with Swagger
|
// Add HTTP support with Swagger
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
@ -201,7 +200,7 @@ app.MapGrpcService<CommandServiceImpl>();
|
|||||||
app.MapGrpcService<QueryServiceImpl>();
|
app.MapGrpcService<QueryServiceImpl>();
|
||||||
app.MapGrpcReflectionService();
|
app.MapGrpcReflectionService();
|
||||||
|
|
||||||
// Map HTTP/REST endpoints
|
// Map HTTP endpoints
|
||||||
app.MapSvrntyCommands();
|
app.MapSvrntyCommands();
|
||||||
app.MapSvrntyQueries();
|
app.MapSvrntyQueries();
|
||||||
|
|
||||||
@ -211,7 +210,7 @@ app.Run();
|
|||||||
**Benefits:**
|
**Benefits:**
|
||||||
- Single codebase supports multiple protocols
|
- Single codebase supports multiple protocols
|
||||||
- gRPC for high-performance, low-latency scenarios (microservices, internal APIs)
|
- gRPC for high-performance, low-latency scenarios (microservices, internal APIs)
|
||||||
- HTTP/REST for web browsers, legacy clients, and public APIs
|
- HTTP for web browsers, legacy clients, and public APIs
|
||||||
- Same commands, queries, and validation logic for both protocols
|
- Same commands, queries, and validation logic for both protocols
|
||||||
- Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients
|
- Swagger UI available for HTTP endpoints, gRPC reflection for gRPC clients
|
||||||
|
|
||||||
|
|||||||
@ -4,10 +4,11 @@
|
|||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
<LangVersion>14</LangVersion>
|
<LangVersion>14</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|||||||
@ -3,11 +3,12 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<IsAotCompatible>false</IsAotCompatible>
|
<IsAotCompatible>false</IsAotCompatible>
|
||||||
<LangVersion>14</LangVersion>
|
<LangVersion>14</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -3,10 +3,11 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<LangVersion>14</LangVersion>
|
<LangVersion>14</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|||||||
@ -3,10 +3,11 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<LangVersion>14</LangVersion>
|
<LangVersion>14</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
namespace Svrnty.CQRS.Grpc;
|
namespace Svrnty.CQRS.Grpc;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -3,12 +3,12 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<IsAotCompatible>false</IsAotCompatible>
|
<IsAotCompatible>false</IsAotCompatible>
|
||||||
<LangVersion>14</LangVersion>
|
<LangVersion>14</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
namespace Svrnty.CQRS.MinimalApi;
|
namespace Svrnty.CQRS.MinimalApi;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Svrnty.CQRS.Configuration;
|
using Svrnty.CQRS.Configuration;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Svrnty.CQRS.Abstractions;
|
using Svrnty.CQRS.Abstractions;
|
||||||
using Svrnty.CQRS.Discovery;
|
using Svrnty.CQRS.Discovery;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|||||||
@ -4,10 +4,11 @@
|
|||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
<LangVersion>14</LangVersion>
|
<LangVersion>14</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<Company>Svrnty</Company>
|
<Company>Svrnty</Company>
|
||||||
<PackageIcon>icon.png</PackageIcon>
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
|
<RepositoryUrl>https://git.openharbor.io/svrnty/dotnet-cqrs</RepositoryUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ builder.WebHost.ConfigureKestrel(options =>
|
|||||||
{
|
{
|
||||||
// Port 6000: HTTP/2 for gRPC
|
// Port 6000: HTTP/2 for gRPC
|
||||||
options.ListenLocalhost(6000, o => o.Protocols = HttpProtocols.Http2);
|
options.ListenLocalhost(6000, o => o.Protocols = HttpProtocols.Http2);
|
||||||
// Port 6001: HTTP/1.1 for REST API
|
// Port 6001: HTTP/1.1 for HTTP API
|
||||||
options.ListenLocalhost(6001, o => o.Protocols = HttpProtocols.Http1);
|
options.ListenLocalhost(6001, o => o.Protocols = HttpProtocols.Http1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user