Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>

<Version>0.6.5</Version>
<Version>0.6.6</Version>
</PropertyGroup>
</Project>
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ app.Run();

A [MinimalEndpoint](#minimalendpoint) is the most straighforward way to define a Minimal Api in REPR format.

Configuration of each endpoint implementation starts with calling one of the MapGet, MapPost, MapPut, MapDelete and MapPatch methods with a route pattern string. The return from any of these methods, a RouteHandlerBuilder instance, can be used to further customize the endpoint like a regular Minimal Api.
Configuration of each endpoint implementation starts with calling one of the MapGet, MapPost, MapPut, MapDelete and MapPatch methods with a route pattern string. The return from any of these methods, a RouteHandlerBuilder instance, can be used to further customize the endpoint similar to a Minimal Api.

The request is processed in 'HandleAsync' method. Request is passed to handler method as parameter after validation (if a validator is registered for request model). Handler method returns a response model or a string or a Minimal Api IResult based response.

Expand Down Expand Up @@ -250,10 +250,10 @@ internal class UploadBook

### Route groups

By default, all endpoints are mapped under root route group. It is possible to define route groups similar to using 'MapGroup' extension method used to map Minimal Apis under a group. Since endpoints are configured by endpoint basis in the 'Configure' method of each endpoint, the approach is a little different than regular Minimal Apis, but these are still Minimal Api route groups and can be configured by any extension method of RouteGroupBuilder. Route groups are also subject to auto discovery and registration, similar to endpoints.
By default, all endpoints are mapped under root route group. It is possible to define route groups similar to using 'MapGroup' extension method used to map Minimal Apis under a group. Since endpoints are configured by endpoint basis in the 'Configure' method of each endpoint, the approach is a little different than configuring a Minimal Api. But route group configuration still utilize Minimal Api route groups and can be decorated by any extension method of RouteGroupBuilder. Route groups are also subject to auto discovery and registration, similar to endpoints.

- [Create a route group implementation](./samples/ShowcaseWebApi/Features/FeaturesRouteGroup.cs) by inheriting RouteGroupConfigurator and implementing 'Configure' method,
- Configuration of each route group implementation starts with calling MapGroup method with a route pattern prefix. The return of 'MapGroup' method, a RouteGroupBuilder instance, can be used to further customize the route group like a regular Minimal Api route group.
- Configuration of each route group implementation starts with calling MapGroup method with a route pattern prefix. The return of 'MapGroup' method, a RouteGroupBuilder instance, can be used to further customize the route group like any Minimal Api route group.
- Apply MapToGroup attribute to either other [route group](./samples/ShowcaseWebApi/Features/Books/Configuration/BooksV1RouteGroup.cs) or [endpoint](./samples/ShowcaseWebApi/Features/Books/CreateBook.cs) classes that will be mapped under created route group. Use type of the new route group implementation as GroupType parameter to the attribute.

Following sample creates a parent route group (FeaturesRouteGroup), a child route group under it (BooksV1RouteGroup) and maps an endpoint (CreateBook) to child route group. Group configuration methods used for this particular sample are all part of Minimal Apis ecosystem and are under [Asp.Versioning](https://github.com/dotnet/aspnet-api-versioning).
Expand Down Expand Up @@ -347,13 +347,13 @@ internal class DisabledCustomerFeature

## Performance

WebResultEndpoints have a slight overhead (3-4%) over regular Minimal Apis on request/sec metric under load tests with 100 virtual users.

MinimalEndpoints perform about same as regular Minimal Apis.
Under load tests with 100 virtual users:
- MinimalEndpoints perform nearly the same (~1%) as Minimal Apis,
- WebResultEndpoints introduce a slight overhead (~2%) compared to Minimal Apis in terms of requests per second.

The web apis called for tests, perform only in-process operations like resolving dependency, validating input, calling local methods with no network or disk I/O.

See [test results](./samples/BenchmarkWebApi/BenchmarkFiles/Results/0.6.5/inprocess_benchmark_results.txt) under [BenchmarkFiles](https://github.com/modabas/ModEndpoints/tree/main/samples/BenchmarkWebApi/BenchmarkFiles) folder of BenchmarkWebApi project for detailed results and test scripts.
See [test results](./samples/BenchmarkWebApi/BenchmarkFiles/Results/0.6.6/inprocess_benchmark_results.txt) under [BenchmarkFiles](https://github.com/modabas/ModEndpoints/tree/main/samples/BenchmarkWebApi/BenchmarkFiles) folder of BenchmarkWebApi project for detailed results and test scripts.

## Endpoint Types

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
 k6  .\k6 run minimal_api_basic.js

/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/

execution: local
script: minimal_api_basic.js
output: -

scenarios: (100.00%) 1 scenario, 100 max VUs, 2m20s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 1m50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


✓ status was 200

checks.........................: 100.00% 8333889 out of 8333889
data_received..................: 1.4 GB 13 MB/s
data_sent......................: 850 MB 7.7 MB/s
http_req_blocked...............: avg=3.33µs min=0s med=0s max=49.27ms p(90)=0s p(95)=0s
http_req_connecting............: avg=8ns min=0s med=0s max=4.52ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=903.53µs min=0s med=999.7µs max=80.18ms p(90)=1.88ms p(95)=2ms
{ expected_response:true }...: avg=903.53µs min=0s med=999.7µs max=80.18ms p(90)=1.88ms p(95)=2ms
http_req_failed................: 0.00% 0 out of 8333889
http_req_receiving.............: avg=32.73µs min=0s med=0s max=74.14ms p(90)=0s p(95)=0s
http_req_sending...............: avg=10.45µs min=0s med=0s max=60.82ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=860.33µs min=0s med=999.5µs max=76.87ms p(90)=1.72ms p(95)=2ms
http_reqs......................: 8333889 75762.281659/s
iteration_duration.............: avg=1ms min=0s med=1ms max=84.13ms p(90)=1.99ms p(95)=2.01ms
iterations.....................: 8333889 75762.281659/s
vus............................: 1 min=1 max=100
vus_max........................: 100 min=100 max=100


running (1m50.0s), 000/100 VUs, 8333889 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs 1m50s


 k6  .\k6 run minimal_endpoint_basic.js

/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/

execution: local
script: minimal_endpoint_basic.js
output: -

scenarios: (100.00%) 1 scenario, 100 max VUs, 2m20s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 1m50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


✓ status was 200

checks.........................: 100.00% 8318368 out of 8318368
data_received..................: 1.4 GB 13 MB/s
data_sent......................: 890 MB 8.1 MB/s
http_req_blocked...............: avg=3.26µs min=0s med=0s max=43.68ms p(90)=0s p(95)=0s
http_req_connecting............: avg=8ns min=0s med=0s max=2.99ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=891µs min=0s med=999.5µs max=97.17ms p(90)=1.82ms p(95)=2ms
{ expected_response:true }...: avg=891µs min=0s med=999.5µs max=97.17ms p(90)=1.82ms p(95)=2ms
http_req_failed................: 0.00% 0 out of 8318368
http_req_receiving.............: avg=32.59µs min=0s med=0s max=71.43ms p(90)=0s p(95)=0s
http_req_sending...............: avg=10.52µs min=0s med=0s max=48.81ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=847.88µs min=0s med=999.2µs max=97.17ms p(90)=1.69ms p(95)=2ms
http_reqs......................: 8318368 75621.204645/s
iteration_duration.............: avg=1ms min=0s med=1ms max=142.24ms p(90)=1.99ms p(95)=2.03ms
iterations.....................: 8318368 75621.204645/s
vus............................: 1 min=1 max=100
vus_max........................: 100 min=100 max=100


running (1m50.0s), 000/100 VUs, 8318368 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs 1m50s


 k6  .\k6 run webresult_endpoint_basic.js

/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/

execution: local
script: webresult_endpoint_basic.js
output: -

scenarios: (100.00%) 1 scenario, 100 max VUs, 2m20s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 1m50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


✓ status was 200

checks.........................: 100.00% 8190023 out of 8190023
data_received..................: 1.4 GB 13 MB/s
data_sent......................: 893 MB 8.1 MB/s
http_req_blocked...............: avg=3.3µs min=0s med=0s max=40.88ms p(90)=0s p(95)=0s
http_req_connecting............: avg=9ns min=0s med=0s max=2.99ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=920.69µs min=0s med=999.8µs max=77.18ms p(90)=1.96ms p(95)=2.01ms
{ expected_response:true }...: avg=920.69µs min=0s med=999.8µs max=77.18ms p(90)=1.96ms p(95)=2.01ms
http_req_failed................: 0.00% 0 out of 8190023
http_req_receiving.............: avg=32.73µs min=0s med=0s max=57.97ms p(90)=0s p(95)=0s
http_req_sending...............: avg=10.55µs min=0s med=0s max=46.61ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=877.41µs min=0s med=999.6µs max=66.38ms p(90)=1.79ms p(95)=2ms
http_reqs......................: 8190023 74454.706015/s
iteration_duration.............: avg=1.02ms min=0s med=1ms max=89.37ms p(90)=1.99ms p(95)=2.02ms
iterations.....................: 8190023 74454.706015/s
vus............................: 1 min=1 max=100
vus_max........................: 100 min=100 max=100


running (1m50.0s), 000/100 VUs, 8190023 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs 1m50s
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
 k6  .\k6 run minimal_api_inprocess.js

/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/

execution: local
script: minimal_api_inprocess.js
output: -

scenarios: (100.00%) 1 scenario, 100 max VUs, 2m20s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 1m50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


✓ status was 200

checks.........................: 100.00% 5854352 out of 5854352
data_received..................: 1.1 GB 10 MB/s
data_sent......................: 1.4 GB 13 MB/s
http_req_blocked...............: avg=4.49µs min=0s med=0s max=74.36ms p(90)=0s p(95)=0s
http_req_connecting............: avg=12ns min=0s med=0s max=2.04ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=1.26ms min=0s med=1.01ms max=131.02ms p(90)=2.05ms p(95)=2.52ms
{ expected_response:true }...: avg=1.26ms min=0s med=1.01ms max=131.02ms p(90)=2.05ms p(95)=2.52ms
http_req_failed................: 0.00% 0 out of 5854352
http_req_receiving.............: avg=51.74µs min=0s med=0s max=123.76ms p(90)=0s p(95)=0s
http_req_sending...............: avg=20.94µs min=0s med=0s max=77.71ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=1.19ms min=0s med=1ms max=93.46ms p(90)=2.01ms p(95)=2.52ms
http_reqs......................: 5854352 53221.183545/s
iteration_duration.............: avg=1.43ms min=0s med=1.01ms max=132.03ms p(90)=2.28ms p(95)=2.85ms
iterations.....................: 5854352 53221.183545/s
vus............................: 1 min=1 max=100
vus_max........................: 100 min=100 max=100


running (1m50.0s), 000/100 VUs, 5854352 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs 1m50s


 k6  .\k6 run minimal_endpoint_inprocess.js

/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/

execution: local
script: minimal_endpoint_inprocess.js
output: -

scenarios: (100.00%) 1 scenario, 100 max VUs, 2m20s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 1m50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


✓ status was 200

checks.........................: 100.00% 5803262 out of 5803262
data_received..................: 1.1 GB 9.9 MB/s
data_sent......................: 1.4 GB 13 MB/s
http_req_blocked...............: avg=4.32µs min=0s med=0s max=39.88ms p(90)=0s p(95)=0s
http_req_connecting............: avg=14ns min=0s med=0s max=9.78ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=1.27ms min=0s med=1.01ms max=84.26ms p(90)=2.07ms p(95)=2.52ms
{ expected_response:true }...: avg=1.27ms min=0s med=1.01ms max=84.26ms p(90)=2.07ms p(95)=2.52ms
http_req_failed................: 0.00% 0 out of 5803262
http_req_receiving.............: avg=47.14µs min=0s med=0s max=82.26ms p(90)=0s p(95)=0s
http_req_sending...............: avg=19.79µs min=0s med=0s max=69.84ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=1.21ms min=0s med=1.01ms max=83.26ms p(90)=2.03ms p(95)=2.52ms
http_reqs......................: 5803262 52756.420044/s
iteration_duration.............: avg=1.44ms min=0s med=1.02ms max=84.26ms p(90)=2.31ms p(95)=2.83ms
iterations.....................: 5803262 52756.420044/s
vus............................: 1 min=1 max=100
vus_max........................: 100 min=100 max=100


running (1m50.0s), 000/100 VUs, 5803262 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs 1m50s


 k6  .\k6 run webresult_endpoint_inprocess.js

/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/

execution: local
script: webresult_endpoint_inprocess.js
output: -

scenarios: (100.00%) 1 scenario, 100 max VUs, 2m20s max duration (incl. graceful stop):
* default: Up to 100 looping VUs for 1m50s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)


✓ status was 200

checks.........................: 100.00% 5767303 out of 5767303
data_received..................: 1.1 GB 9.8 MB/s
data_sent......................: 1.4 GB 13 MB/s
http_req_blocked...............: avg=4.44µs min=0s med=0s max=53.62ms p(90)=0s p(95)=0s
http_req_connecting............: avg=14ns min=0s med=0s max=6.82ms p(90)=0s p(95)=0s
✓ http_req_duration..............: avg=1.29ms min=0s med=1.01ms max=91.78ms p(90)=2.1ms p(95)=2.53ms
{ expected_response:true }...: avg=1.29ms min=0s med=1.01ms max=91.78ms p(90)=2.1ms p(95)=2.53ms
http_req_failed................: 0.00% 0 out of 5767303
http_req_receiving.............: avg=48.93µs min=0s med=0s max=82.25ms p(90)=0s p(95)=0s
http_req_sending...............: avg=20.41µs min=0s med=0s max=66.9ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=1.22ms min=0s med=1.01ms max=91.78ms p(90)=2.05ms p(95)=2.52ms
http_reqs......................: 5767303 52429.944719/s
iteration_duration.............: avg=1.45ms min=0s med=1.09ms max=108.91ms p(90)=2.34ms p(95)=2.82ms
iterations.....................: 5767303 52429.944719/s
vus............................: 1 min=1 max=100
vus_max........................: 100 min=100 max=100


running (1m50.0s), 000/100 VUs, 5767303 complete and 0 interrupted iterations
default ✓ [======================================] 000/100 VUs 1m50s
4 changes: 4 additions & 0 deletions src/ModEndpoints/DependencyInjectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public static IServiceCollection AddModEndpointsFromAssembly(
//WebResultEndpoint components
services.TryAddKeyedSingleton<IResultToResponseMapper, DefaultResultToResponseMapper>(
WebResultEndpointDefinitions.DefaultResultToResponseMapperName);
services.TryAddKeyedSingleton<IPreferredSuccessStatusCodeCache, PreferredSuccessStatusCodeCacheForResult>(
WebResultEndpointDefinitions.PreferredSuccessStatusCodeCacheNameForResult);
services.TryAddKeyedSingleton<IPreferredSuccessStatusCodeCache, PreferredSuccessStatusCodeCacheForResultOfT>(
WebResultEndpointDefinitions.PreferredSuccessStatusCodeCacheNameForResultOfT);
services.TryAddScoped<ILocationStore, DefaultLocationStore>();
services.TryAddSingleton<IResultToResponseMapProvider, DefaultResultToResponseMapProvider>();

Expand Down
Loading