@ngxtm/devkit 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/rules/README.md +141 -0
- package/rules/dart/best-practices/SKILL.md +23 -0
- package/rules/dart/language/SKILL.md +52 -0
- package/rules/dart/tooling/SKILL.md +43 -0
- package/rules/dotnet/aspnet-core/SKILL.md +92 -0
- package/rules/dotnet/aspnet-core/references/REFERENCE.md +335 -0
- package/rules/dotnet/best-practices/SKILL.md +101 -0
- package/rules/dotnet/best-practices/references/REFERENCE.md +256 -0
- package/rules/dotnet/blazor/SKILL.md +146 -0
- package/rules/dotnet/blazor/references/REFERENCE.md +392 -0
- package/rules/dotnet/language/SKILL.md +82 -0
- package/rules/dotnet/language/references/REFERENCE.md +222 -0
- package/rules/dotnet/patterns.rule.md +388 -0
- package/rules/dotnet/razor-pages/SKILL.md +124 -0
- package/rules/dotnet/razor-pages/references/REFERENCE.md +321 -0
- package/rules/dotnet/security/SKILL.md +89 -0
- package/rules/dotnet/security/references/REFERENCE.md +295 -0
- package/rules/dotnet/tooling/SKILL.md +92 -0
- package/rules/dotnet/tooling/references/REFERENCE.md +300 -0
- package/rules/flutter/auto-route-navigation/SKILL.md +43 -0
- package/rules/flutter/auto-route-navigation/references/REFERENCE.md +19 -0
- package/rules/flutter/auto-route-navigation/references/router-config.md +62 -0
- package/rules/flutter/bloc-state-management/SKILL.md +64 -0
- package/rules/flutter/bloc-state-management/references/REFERENCE.md +20 -0
- package/rules/flutter/bloc-state-management/references/auth-bloc-example.md +52 -0
- package/rules/flutter/bloc-state-management/references/equatable-usage.md +56 -0
- package/rules/flutter/bloc-state-management/references/property-based-state.md +68 -0
- package/rules/flutter/bloc.rule.md +76 -0
- package/rules/flutter/cicd/SKILL.md +48 -0
- package/rules/flutter/cicd/references/advanced-workflow.md +66 -0
- package/rules/flutter/cicd/references/fastlane.md +139 -0
- package/rules/flutter/cicd/references/github-actions.md +59 -0
- package/rules/flutter/dependency-injection/SKILL.md +42 -0
- package/rules/flutter/dependency-injection/references/REFERENCE.md +15 -0
- package/rules/flutter/dependency-injection/references/modules.md +37 -0
- package/rules/flutter/error-handling/SKILL.md +32 -0
- package/rules/flutter/error-handling/references/REFERENCE.md +19 -0
- package/rules/flutter/error-handling/references/error-mapping.md +31 -0
- package/rules/flutter/feature-based-clean-architecture/SKILL.md +46 -0
- package/rules/flutter/feature-based-clean-architecture/references/REFERENCE.md +14 -0
- package/rules/flutter/feature-based-clean-architecture/references/folder-structure.md +36 -0
- package/rules/flutter/getx-navigation/SKILL.md +70 -0
- package/rules/flutter/getx-navigation/references/app-pages.md +40 -0
- package/rules/flutter/getx-navigation/references/middleware-example.md +29 -0
- package/rules/flutter/getx-state-management/SKILL.md +76 -0
- package/rules/flutter/getx-state-management/references/binding-example.md +32 -0
- package/rules/flutter/getx-state-management/references/reactive-vs-simple.md +39 -0
- package/rules/flutter/go-router-navigation/SKILL.md +57 -0
- package/rules/flutter/idiomatic-flutter/SKILL.md +20 -0
- package/rules/flutter/layer-based-clean-architecture/SKILL.md +50 -0
- package/rules/flutter/layer-based-clean-architecture/references/REFERENCE.md +60 -0
- package/rules/flutter/layer-based-clean-architecture/references/repository-mapping.md +50 -0
- package/rules/flutter/localization/SKILL.md +50 -0
- package/rules/flutter/localization/references/REFERENCE.md +48 -0
- package/rules/flutter/localization/references/sheet-loader.md +33 -0
- package/rules/flutter/navigator-v1-navigation/SKILL.md +71 -0
- package/rules/flutter/navigator-v1-navigation/references/on-generate-route.md +48 -0
- package/rules/flutter/performance/SKILL.md +24 -0
- package/rules/flutter/retrofit-networking/SKILL.md +51 -0
- package/rules/flutter/retrofit-networking/references/REFERENCE.md +19 -0
- package/rules/flutter/retrofit-networking/references/token-refresh.md +40 -0
- package/rules/flutter/riverpod-state-management/SKILL.md +53 -0
- package/rules/flutter/riverpod-state-management/references/architecture.md +124 -0
- package/rules/flutter/riverpod-state-management/references/best-practices.md +89 -0
- package/rules/flutter/riverpod-state-management/references/testing.md +73 -0
- package/rules/flutter/riverpod.rule.md +78 -0
- package/rules/flutter/security/SKILL.md +33 -0
- package/rules/flutter/security/references/REFERENCE.md +15 -0
- package/rules/flutter/security/references/network-security.md +28 -0
- package/rules/flutter/testing/SKILL.md +44 -0
- package/rules/flutter/testing/references/REFERENCE.md +21 -0
- package/rules/flutter/testing/references/bloc-testing.md +38 -0
- package/rules/flutter/testing/references/integration-testing.md +128 -0
- package/rules/flutter/testing/references/robot-pattern.md +82 -0
- package/rules/flutter/testing/references/unit-testing.md +130 -0
- package/rules/flutter/testing/references/widget-testing.md +120 -0
- package/rules/flutter/widgets/SKILL.md +37 -0
- package/rules/golang/chi-router/SKILL.md +219 -0
- package/rules/golang/chi-router/references/REFERENCE.md +13 -0
- package/rules/golang/chi-router/references/routing-patterns.md +205 -0
- package/rules/golang/cobra-cli/SKILL.md +227 -0
- package/rules/golang/cobra-cli/references/REFERENCE.md +13 -0
- package/rules/golang/cobra-cli/references/command-patterns.md +224 -0
- package/rules/golang/core/SKILL.md +210 -0
- package/rules/golang/core/references/REFERENCE.md +14 -0
- package/rules/golang/core/references/concurrency-patterns.md +114 -0
- package/rules/golang/core/references/error-handling.md +87 -0
- package/rules/golang/echo-framework/SKILL.md +215 -0
- package/rules/golang/echo-framework/references/REFERENCE.md +14 -0
- package/rules/golang/echo-framework/references/middleware-patterns.md +141 -0
- package/rules/golang/echo-framework/references/routing-patterns.md +140 -0
- package/rules/golang/ent-orm/SKILL.md +239 -0
- package/rules/golang/ent-orm/references/REFERENCE.md +13 -0
- package/rules/golang/ent-orm/references/schema-patterns.md +255 -0
- package/rules/golang/fiber-framework/SKILL.md +196 -0
- package/rules/golang/fiber-framework/references/REFERENCE.md +13 -0
- package/rules/golang/fiber-framework/references/routing-patterns.md +191 -0
- package/rules/golang/gin-framework/SKILL.md +205 -0
- package/rules/golang/gin-framework/references/REFERENCE.md +14 -0
- package/rules/golang/gin-framework/references/middleware-patterns.md +119 -0
- package/rules/golang/gorm-orm/SKILL.md +196 -0
- package/rules/golang/gorm-orm/references/REFERENCE.md +14 -0
- package/rules/golang/gorm-orm/references/model-definitions.md +167 -0
- package/rules/golang/gorm-orm/references/query-patterns.md +161 -0
- package/rules/golang/grpc/SKILL.md +231 -0
- package/rules/golang/grpc/references/REFERENCE.md +13 -0
- package/rules/golang/grpc/references/service-patterns.md +276 -0
- package/rules/golang/testify/SKILL.md +239 -0
- package/rules/golang/testify/references/REFERENCE.md +13 -0
- package/rules/golang/testify/references/assert-patterns.md +170 -0
- package/rules/golang/validator/SKILL.md +234 -0
- package/rules/golang/validator/references/REFERENCE.md +13 -0
- package/rules/golang/validator/references/validation-tags.md +211 -0
- package/rules/golang/viper-config/SKILL.md +244 -0
- package/rules/golang/viper-config/references/REFERENCE.md +13 -0
- package/rules/golang/viper-config/references/config-loading.md +181 -0
- package/rules/golang/wire-di/SKILL.md +243 -0
- package/rules/golang/wire-di/references/REFERENCE.md +13 -0
- package/rules/golang/wire-di/references/provider-patterns.md +193 -0
- package/rules/golang/zap-logging/SKILL.md +203 -0
- package/rules/golang/zap-logging/references/REFERENCE.md +13 -0
- package/rules/golang/zap-logging/references/logger-config.md +165 -0
- package/rules/java/build-gradle/SKILL.md +92 -0
- package/rules/java/build-gradle/references/REFERENCE.md +14 -0
- package/rules/java/build-gradle/references/kotlin-dsl.md +118 -0
- package/rules/java/build-gradle/references/task-configuration.md +132 -0
- package/rules/java/build-maven/SKILL.md +86 -0
- package/rules/java/build-maven/references/REFERENCE.md +14 -0
- package/rules/java/build-maven/references/dependency-management.md +111 -0
- package/rules/java/build-maven/references/lifecycle-phases.md +114 -0
- package/rules/java/graalvm-native/SKILL.md +105 -0
- package/rules/java/graalvm-native/references/REFERENCE.md +12 -0
- package/rules/java/java-collections-streams/SKILL.md +148 -0
- package/rules/java/java-collections-streams/references/REFERENCE.md +15 -0
- package/rules/java/java-collections-streams/references/collectors-patterns.md +178 -0
- package/rules/java/java-collections-streams/references/stream-pipelines.md +165 -0
- package/rules/java/java-concurrency/SKILL.md +187 -0
- package/rules/java/java-concurrency/references/REFERENCE.md +17 -0
- package/rules/java/java-concurrency/references/completable-future.md +165 -0
- package/rules/java/java-concurrency/references/executor-patterns.md +176 -0
- package/rules/java/java-concurrency/references/virtual-threads.md +190 -0
- package/rules/java/java-core-language/SKILL.md +121 -0
- package/rules/java/java-core-language/references/REFERENCE.md +15 -0
- package/rules/java/java-core-language/references/jvm-memory-model.md +160 -0
- package/rules/java/java-core-language/references/modern-java-features.md +168 -0
- package/rules/java/java-project-structure/SKILL.md +195 -0
- package/rules/java/java-project-structure/references/REFERENCE.md +15 -0
- package/rules/java/java-project-structure/references/maven-project-layout.md +199 -0
- package/rules/java/java-project-structure/references/module-system.md +159 -0
- package/rules/java/micronaut-core/SKILL.md +99 -0
- package/rules/java/micronaut-core/references/REFERENCE.md +12 -0
- package/rules/java/micronaut-reactive/SKILL.md +68 -0
- package/rules/java/micronaut-reactive/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-core/SKILL.md +85 -0
- package/rules/java/quarkus-core/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-reactive/SKILL.md +67 -0
- package/rules/java/quarkus-reactive/references/REFERENCE.md +12 -0
- package/rules/java/spring-batch/SKILL.md +102 -0
- package/rules/java/spring-batch/references/REFERENCE.md +12 -0
- package/rules/java/spring-boot-architecture/SKILL.md +206 -0
- package/rules/java/spring-boot-architecture/references/REFERENCE.md +15 -0
- package/rules/java/spring-boot-architecture/references/auto-configuration.md +158 -0
- package/rules/java/spring-boot-architecture/references/configuration-properties.md +202 -0
- package/rules/java/spring-boot-web/SKILL.md +217 -0
- package/rules/java/spring-boot-web/references/REFERENCE.md +17 -0
- package/rules/java/spring-cloud/SKILL.md +109 -0
- package/rules/java/spring-cloud/references/REFERENCE.md +13 -0
- package/rules/java/spring-data-jpa/SKILL.md +241 -0
- package/rules/java/spring-data-jpa/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/SKILL.md +161 -0
- package/rules/java/spring-security/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/references/jwt-auth-flow.md +213 -0
- package/rules/java/testing-junit-mockito/SKILL.md +135 -0
- package/rules/java/testing-junit-mockito/references/REFERENCE.md +15 -0
- package/rules/java/testing-junit-mockito/references/junit5-patterns.md +159 -0
- package/rules/java/testing-junit-mockito/references/mockito-patterns.md +148 -0
- package/rules/java/testing-junit-mockito/references/spring-boot-testing.md +152 -0
- package/rules/javascript/best-practices/SKILL.md +64 -0
- package/rules/javascript/best-practices/references/REFERENCE.md +91 -0
- package/rules/javascript/language/SKILL.md +71 -0
- package/rules/javascript/language/references/REFERENCE.md +106 -0
- package/rules/javascript/tooling/SKILL.md +60 -0
- package/rules/javascript/tooling/references/REFERENCE.md +107 -0
- package/rules/metadata.json +54 -0
- package/rules/nestjs/api-standards/SKILL.md +47 -0
- package/rules/nestjs/api-standards/references/pagination-wrapper.md +87 -0
- package/rules/nestjs/architecture/SKILL.md +68 -0
- package/rules/nestjs/architecture/references/dynamic-module.md +53 -0
- package/rules/nestjs/caching/SKILL.md +51 -0
- package/rules/nestjs/caching/references/REFERENCE.md +13 -0
- package/rules/nestjs/caching/references/cache-patterns.md +183 -0
- package/rules/nestjs/configuration/SKILL.md +41 -0
- package/rules/nestjs/configuration/references/REFERENCE.md +13 -0
- package/rules/nestjs/configuration/references/config-patterns.md +184 -0
- package/rules/nestjs/controllers-services/SKILL.md +63 -0
- package/rules/nestjs/controllers-services/references/REFERENCE.md +14 -0
- package/rules/nestjs/controllers-services/references/controller-patterns.md +119 -0
- package/rules/nestjs/controllers-services/references/service-patterns.md +129 -0
- package/rules/nestjs/database/SKILL.md +102 -0
- package/rules/nestjs/database/references/REFERENCE.md +14 -0
- package/rules/nestjs/database/references/typeorm-patterns.md +156 -0
- package/rules/nestjs/deployment/SKILL.md +36 -0
- package/rules/nestjs/deployment/references/REFERENCE.md +13 -0
- package/rules/nestjs/deployment/references/deployment-patterns.md +140 -0
- package/rules/nestjs/documentation/SKILL.md +64 -0
- package/rules/nestjs/documentation/references/REFERENCE.md +13 -0
- package/rules/nestjs/documentation/references/swagger-patterns.md +139 -0
- package/rules/nestjs/error-handling/SKILL.md +55 -0
- package/rules/nestjs/error-handling/references/REFERENCE.md +13 -0
- package/rules/nestjs/error-handling/references/exception-filters.md +152 -0
- package/rules/nestjs/file-uploads/SKILL.md +35 -0
- package/rules/nestjs/file-uploads/references/REFERENCE.md +13 -0
- package/rules/nestjs/file-uploads/references/upload-patterns.md +125 -0
- package/rules/nestjs/observability/SKILL.md +39 -0
- package/rules/nestjs/observability/references/REFERENCE.md +13 -0
- package/rules/nestjs/observability/references/logging-metrics.md +175 -0
- package/rules/nestjs/performance/SKILL.md +60 -0
- package/rules/nestjs/performance/references/REFERENCE.md +13 -0
- package/rules/nestjs/performance/references/performance-patterns.md +107 -0
- package/rules/nestjs/real-time/SKILL.md +45 -0
- package/rules/nestjs/real-time/references/REFERENCE.md +13 -0
- package/rules/nestjs/real-time/references/websocket-patterns.md +121 -0
- package/rules/nestjs/scheduling/SKILL.md +39 -0
- package/rules/nestjs/scheduling/references/REFERENCE.md +13 -0
- package/rules/nestjs/scheduling/references/scheduling-patterns.md +137 -0
- package/rules/nestjs/search/SKILL.md +41 -0
- package/rules/nestjs/search/references/REFERENCE.md +13 -0
- package/rules/nestjs/search/references/search-patterns.md +137 -0
- package/rules/nestjs/security/SKILL.md +87 -0
- package/rules/nestjs/security/references/REFERENCE.md +14 -0
- package/rules/nestjs/security/references/authentication.md +151 -0
- package/rules/nestjs/testing/SKILL.md +40 -0
- package/rules/nestjs/testing/references/REFERENCE.md +14 -0
- package/rules/nestjs/testing/references/unit-testing.md +179 -0
- package/rules/nestjs/transport/SKILL.md +45 -0
- package/rules/nestjs/transport/references/REFERENCE.md +13 -0
- package/rules/nestjs/transport/references/microservices-patterns.md +170 -0
- package/rules/nextjs/app-router/SKILL.md +46 -0
- package/rules/nextjs/app-router/references/REFERENCE.md +14 -0
- package/rules/nextjs/app-router/references/routing-patterns.md +182 -0
- package/rules/nextjs/architecture/SKILL.md +44 -0
- package/rules/nextjs/architecture/references/fsd-structure.md +77 -0
- package/rules/nextjs/authentication/SKILL.md +29 -0
- package/rules/nextjs/authentication/references/auth-implementation.md +73 -0
- package/rules/nextjs/caching/SKILL.md +66 -0
- package/rules/nextjs/caching/references/REFERENCE.md +13 -0
- package/rules/nextjs/caching/references/cache-strategies.md +168 -0
- package/rules/nextjs/data-access-layer/SKILL.md +33 -0
- package/rules/nextjs/data-access-layer/references/patterns.md +66 -0
- package/rules/nextjs/data-fetching/SKILL.md +59 -0
- package/rules/nextjs/data-fetching/references/REFERENCE.md +13 -0
- package/rules/nextjs/data-fetching/references/fetch-patterns.md +160 -0
- package/rules/nextjs/internationalization/SKILL.md +105 -0
- package/rules/nextjs/internationalization/references/REFERENCE.md +13 -0
- package/rules/nextjs/internationalization/references/i18n-patterns.md +180 -0
- package/rules/nextjs/optimization/SKILL.md +64 -0
- package/rules/nextjs/optimization/references/REFERENCE.md +13 -0
- package/rules/nextjs/optimization/references/optimization-patterns.md +190 -0
- package/rules/nextjs/rendering/SKILL.md +91 -0
- package/rules/nextjs/rendering/references/REFERENCE.md +13 -0
- package/rules/nextjs/rendering/references/rendering-modes.md +163 -0
- package/rules/nextjs/server-actions/SKILL.md +46 -0
- package/rules/nextjs/server-actions/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-actions/references/action-patterns.md +188 -0
- package/rules/nextjs/server-components/SKILL.md +52 -0
- package/rules/nextjs/server-components/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-components/references/component-patterns.md +175 -0
- package/rules/nextjs/state-management/SKILL.md +73 -0
- package/rules/nextjs/state-management/references/REFERENCE.md +13 -0
- package/rules/nextjs/state-management/references/state-patterns.md +218 -0
- package/rules/nextjs/styling/SKILL.md +31 -0
- package/rules/nextjs/styling/references/implementation.md +56 -0
- package/rules/react/component-patterns/SKILL.md +66 -0
- package/rules/react/component-patterns/references/REFERENCE.md +126 -0
- package/rules/react/hooks/SKILL.md +60 -0
- package/rules/react/hooks/references/REFERENCE.md +132 -0
- package/rules/react/hooks.rule.md +79 -0
- package/rules/react/performance/SKILL.md +69 -0
- package/rules/react/performance/references/REFERENCE.md +143 -0
- package/rules/react/security/SKILL.md +46 -0
- package/rules/react/security/references/REFERENCE.md +170 -0
- package/rules/react/state-management/SKILL.md +56 -0
- package/rules/react/state-management/references/REFERENCE.md +137 -0
- package/rules/react/testing/SKILL.md +45 -0
- package/rules/react/testing/references/REFERENCE.md +149 -0
- package/rules/react/tooling/SKILL.md +39 -0
- package/rules/react/typescript/SKILL.md +53 -0
- package/rules/rust/actix-web/SKILL.md +160 -0
- package/rules/rust/actix-web/references/REFERENCE.md +13 -0
- package/rules/rust/actix-web/references/handler-patterns.md +198 -0
- package/rules/rust/async-graphql/SKILL.md +228 -0
- package/rules/rust/async-graphql/references/REFERENCE.md +13 -0
- package/rules/rust/async-graphql/references/schema-patterns.md +215 -0
- package/rules/rust/axum/SKILL.md +161 -0
- package/rules/rust/axum/references/REFERENCE.md +14 -0
- package/rules/rust/axum/references/handler-patterns.md +97 -0
- package/rules/rust/bevy/SKILL.md +206 -0
- package/rules/rust/bevy/references/REFERENCE.md +13 -0
- package/rules/rust/bevy/references/ecs-patterns.md +226 -0
- package/rules/rust/clap/SKILL.md +217 -0
- package/rules/rust/clap/references/REFERENCE.md +13 -0
- package/rules/rust/clap/references/derive-patterns.md +205 -0
- package/rules/rust/core/SKILL.md +154 -0
- package/rules/rust/core/references/REFERENCE.md +14 -0
- package/rules/rust/core/references/error-handling.md +92 -0
- package/rules/rust/diesel-orm/SKILL.md +176 -0
- package/rules/rust/diesel-orm/references/REFERENCE.md +13 -0
- package/rules/rust/diesel-orm/references/schema-patterns.md +206 -0
- package/rules/rust/rocket/SKILL.md +182 -0
- package/rules/rust/rocket/references/REFERENCE.md +13 -0
- package/rules/rust/rocket/references/handler-patterns.md +209 -0
- package/rules/rust/sea-orm/SKILL.md +230 -0
- package/rules/rust/sea-orm/references/REFERENCE.md +13 -0
- package/rules/rust/sea-orm/references/entity-patterns.md +221 -0
- package/rules/rust/serde-serialization/SKILL.md +150 -0
- package/rules/rust/serde-serialization/references/REFERENCE.md +13 -0
- package/rules/rust/serde-serialization/references/serialization-patterns.md +199 -0
- package/rules/rust/sqlx-database/SKILL.md +140 -0
- package/rules/rust/sqlx-database/references/REFERENCE.md +13 -0
- package/rules/rust/sqlx-database/references/query-patterns.md +210 -0
- package/rules/rust/tauri/SKILL.md +180 -0
- package/rules/rust/tauri/references/REFERENCE.md +13 -0
- package/rules/rust/tauri/references/command-patterns.md +209 -0
- package/rules/rust/tokio-runtime/SKILL.md +167 -0
- package/rules/rust/tokio-runtime/references/REFERENCE.md +14 -0
- package/rules/rust/tokio-runtime/references/async-patterns.md +137 -0
- package/rules/rust/tokio-runtime/references/synchronization.md +152 -0
- package/rules/rust/tonic/SKILL.md +231 -0
- package/rules/rust/tonic/references/REFERENCE.md +13 -0
- package/rules/rust/tonic/references/service-patterns.md +213 -0
- package/rules/rust/tracing/SKILL.md +214 -0
- package/rules/rust/tracing/references/REFERENCE.md +13 -0
- package/rules/rust/tracing/references/instrumentation.md +187 -0
- package/rules/typescript/best-practices/SKILL.md +108 -0
- package/rules/typescript/best-practices/references/REFERENCE.md +68 -0
- package/rules/typescript/language/SKILL.md +72 -0
- package/rules/typescript/language/references/REFERENCE.md +67 -0
- package/rules/typescript/patterns.rule.md +85 -0
- package/rules/typescript/security/SKILL.md +59 -0
- package/rules/typescript/security/references/REFERENCE.md +113 -0
- package/rules/typescript/tooling/SKILL.md +52 -0
- package/rules/typescript/tooling/references/REFERENCE.md +110 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# .NET Security Reference
|
|
2
|
+
|
|
3
|
+
Authentication, authorization, and security headers configuration.
|
|
4
|
+
|
|
5
|
+
## References
|
|
6
|
+
|
|
7
|
+
- [**JWT Authentication**](jwt-authentication.md) - Token generation and validation.
|
|
8
|
+
- [**Identity Configuration**](identity-configuration.md) - Password rules, lockout settings.
|
|
9
|
+
- [**Security Headers**](security-headers.md) - CSP, HSTS, X-Frame-Options.
|
|
10
|
+
|
|
11
|
+
## JWT Token Generation
|
|
12
|
+
|
|
13
|
+
```csharp
|
|
14
|
+
public class TokenService(IOptions<JwtSettings> options)
|
|
15
|
+
{
|
|
16
|
+
private readonly JwtSettings _settings = options.Value;
|
|
17
|
+
|
|
18
|
+
public string GenerateToken(User user)
|
|
19
|
+
{
|
|
20
|
+
var claims = new[]
|
|
21
|
+
{
|
|
22
|
+
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
|
23
|
+
new Claim(ClaimTypes.Email, user.Email),
|
|
24
|
+
new Claim(ClaimTypes.Role, user.Role),
|
|
25
|
+
new Claim("tenant_id", user.TenantId.ToString())
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Key));
|
|
29
|
+
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
|
30
|
+
|
|
31
|
+
var token = new JwtSecurityToken(
|
|
32
|
+
issuer: _settings.Issuer,
|
|
33
|
+
audience: _settings.Audience,
|
|
34
|
+
claims: claims,
|
|
35
|
+
expires: DateTime.UtcNow.AddHours(_settings.ExpiryHours),
|
|
36
|
+
signingCredentials: credentials);
|
|
37
|
+
|
|
38
|
+
return new JwtSecurityTokenHandler().WriteToken(token);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public ClaimsPrincipal? ValidateToken(string token)
|
|
42
|
+
{
|
|
43
|
+
var handler = new JwtSecurityTokenHandler();
|
|
44
|
+
try
|
|
45
|
+
{
|
|
46
|
+
return handler.ValidateToken(token, new TokenValidationParameters
|
|
47
|
+
{
|
|
48
|
+
ValidateIssuer = true,
|
|
49
|
+
ValidateAudience = true,
|
|
50
|
+
ValidateLifetime = true,
|
|
51
|
+
ValidateIssuerSigningKey = true,
|
|
52
|
+
ValidIssuer = _settings.Issuer,
|
|
53
|
+
ValidAudience = _settings.Audience,
|
|
54
|
+
IssuerSigningKey = new SymmetricSecurityKey(
|
|
55
|
+
Encoding.UTF8.GetBytes(_settings.Key)),
|
|
56
|
+
ClockSkew = TimeSpan.Zero
|
|
57
|
+
}, out _);
|
|
58
|
+
}
|
|
59
|
+
catch
|
|
60
|
+
{
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## ASP.NET Identity Configuration
|
|
68
|
+
|
|
69
|
+
```csharp
|
|
70
|
+
// Program.cs
|
|
71
|
+
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
|
72
|
+
{
|
|
73
|
+
// Password requirements
|
|
74
|
+
options.Password.RequiredLength = 12;
|
|
75
|
+
options.Password.RequireDigit = true;
|
|
76
|
+
options.Password.RequireLowercase = true;
|
|
77
|
+
options.Password.RequireUppercase = true;
|
|
78
|
+
options.Password.RequireNonAlphanumeric = true;
|
|
79
|
+
options.Password.RequiredUniqueChars = 4;
|
|
80
|
+
|
|
81
|
+
// Lockout settings
|
|
82
|
+
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
|
|
83
|
+
options.Lockout.MaxFailedAccessAttempts = 5;
|
|
84
|
+
options.Lockout.AllowedForNewUsers = true;
|
|
85
|
+
|
|
86
|
+
// User settings
|
|
87
|
+
options.User.RequireUniqueEmail = true;
|
|
88
|
+
options.SignIn.RequireConfirmedEmail = true;
|
|
89
|
+
})
|
|
90
|
+
.AddEntityFrameworkStores<AppDbContext>()
|
|
91
|
+
.AddDefaultTokenProviders();
|
|
92
|
+
|
|
93
|
+
// Password hasher upgrade (Argon2id recommended for new apps)
|
|
94
|
+
builder.Services.Configure<PasswordHasherOptions>(options =>
|
|
95
|
+
{
|
|
96
|
+
options.IterationCount = 310000; // OWASP recommendation
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Security Headers Middleware
|
|
101
|
+
|
|
102
|
+
```csharp
|
|
103
|
+
// Using a middleware class
|
|
104
|
+
public class SecurityHeadersMiddleware(RequestDelegate next)
|
|
105
|
+
{
|
|
106
|
+
public async Task InvokeAsync(HttpContext context)
|
|
107
|
+
{
|
|
108
|
+
// Prevent clickjacking
|
|
109
|
+
context.Response.Headers.Append("X-Frame-Options", "DENY");
|
|
110
|
+
|
|
111
|
+
// Prevent MIME sniffing
|
|
112
|
+
context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
|
|
113
|
+
|
|
114
|
+
// XSS protection (legacy, CSP is better)
|
|
115
|
+
context.Response.Headers.Append("X-XSS-Protection", "1; mode=block");
|
|
116
|
+
|
|
117
|
+
// Referrer policy
|
|
118
|
+
context.Response.Headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
119
|
+
|
|
120
|
+
// Content Security Policy
|
|
121
|
+
context.Response.Headers.Append("Content-Security-Policy",
|
|
122
|
+
"default-src 'self'; " +
|
|
123
|
+
"script-src 'self' 'unsafe-inline'; " +
|
|
124
|
+
"style-src 'self' 'unsafe-inline'; " +
|
|
125
|
+
"img-src 'self' data: https:; " +
|
|
126
|
+
"font-src 'self'; " +
|
|
127
|
+
"frame-ancestors 'none';");
|
|
128
|
+
|
|
129
|
+
// Permissions Policy
|
|
130
|
+
context.Response.Headers.Append("Permissions-Policy",
|
|
131
|
+
"geolocation=(), microphone=(), camera=()");
|
|
132
|
+
|
|
133
|
+
await next(context);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Usage in Program.cs
|
|
138
|
+
app.UseMiddleware<SecurityHeadersMiddleware>();
|
|
139
|
+
app.UseHsts(); // Strict-Transport-Security
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## CORS Configuration
|
|
143
|
+
|
|
144
|
+
```csharp
|
|
145
|
+
// Development (allow specific origins)
|
|
146
|
+
builder.Services.AddCors(options =>
|
|
147
|
+
{
|
|
148
|
+
options.AddPolicy("Development", policy =>
|
|
149
|
+
{
|
|
150
|
+
policy.WithOrigins("http://localhost:3000", "http://localhost:5173")
|
|
151
|
+
.AllowAnyHeader()
|
|
152
|
+
.AllowAnyMethod()
|
|
153
|
+
.AllowCredentials();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
options.AddPolicy("Production", policy =>
|
|
157
|
+
{
|
|
158
|
+
policy.WithOrigins(
|
|
159
|
+
builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>()!)
|
|
160
|
+
.WithHeaders("Authorization", "Content-Type", "X-Requested-With")
|
|
161
|
+
.WithMethods("GET", "POST", "PUT", "DELETE")
|
|
162
|
+
.AllowCredentials()
|
|
163
|
+
.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Usage
|
|
168
|
+
app.UseCors(builder.Environment.IsDevelopment() ? "Development" : "Production");
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Resource-Based Authorization
|
|
172
|
+
|
|
173
|
+
```csharp
|
|
174
|
+
// Requirement
|
|
175
|
+
public class ResourceOwnerRequirement : IAuthorizationRequirement { }
|
|
176
|
+
|
|
177
|
+
// Handler
|
|
178
|
+
public class ResourceOwnerHandler : AuthorizationHandler<ResourceOwnerRequirement, Order>
|
|
179
|
+
{
|
|
180
|
+
protected override Task HandleRequirementAsync(
|
|
181
|
+
AuthorizationHandlerContext context,
|
|
182
|
+
ResourceOwnerRequirement requirement,
|
|
183
|
+
Order resource)
|
|
184
|
+
{
|
|
185
|
+
var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
186
|
+
|
|
187
|
+
if (resource.UserId.ToString() == userId ||
|
|
188
|
+
context.User.IsInRole("Admin"))
|
|
189
|
+
{
|
|
190
|
+
context.Succeed(requirement);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return Task.CompletedTask;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Registration
|
|
198
|
+
builder.Services.AddScoped<IAuthorizationHandler, ResourceOwnerHandler>();
|
|
199
|
+
|
|
200
|
+
// Usage in controller
|
|
201
|
+
public async Task<IActionResult> UpdateOrder(int id, UpdateOrderDto dto)
|
|
202
|
+
{
|
|
203
|
+
var order = await _orderService.GetByIdAsync(id);
|
|
204
|
+
if (order is null) return NotFound();
|
|
205
|
+
|
|
206
|
+
var authResult = await _authService.AuthorizeAsync(
|
|
207
|
+
User, order, new ResourceOwnerRequirement());
|
|
208
|
+
|
|
209
|
+
if (!authResult.Succeeded) return Forbid();
|
|
210
|
+
|
|
211
|
+
await _orderService.UpdateAsync(order, dto);
|
|
212
|
+
return NoContent();
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Rate Limiting (.NET 7+)
|
|
217
|
+
|
|
218
|
+
```csharp
|
|
219
|
+
builder.Services.AddRateLimiter(options =>
|
|
220
|
+
{
|
|
221
|
+
// Fixed window: 100 requests per minute
|
|
222
|
+
options.AddFixedWindowLimiter("fixed", cfg =>
|
|
223
|
+
{
|
|
224
|
+
cfg.Window = TimeSpan.FromMinutes(1);
|
|
225
|
+
cfg.PermitLimit = 100;
|
|
226
|
+
cfg.QueueLimit = 10;
|
|
227
|
+
cfg.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Sliding window: smoother rate limiting
|
|
231
|
+
options.AddSlidingWindowLimiter("sliding", cfg =>
|
|
232
|
+
{
|
|
233
|
+
cfg.Window = TimeSpan.FromMinutes(1);
|
|
234
|
+
cfg.PermitLimit = 100;
|
|
235
|
+
cfg.SegmentsPerWindow = 6; // 10-second segments
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Token bucket: burst allowance
|
|
239
|
+
options.AddTokenBucketLimiter("token", cfg =>
|
|
240
|
+
{
|
|
241
|
+
cfg.TokenLimit = 100;
|
|
242
|
+
cfg.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
|
|
243
|
+
cfg.TokensPerPeriod = 20;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Per-user limiting
|
|
247
|
+
options.AddPolicy("per-user", context =>
|
|
248
|
+
RateLimitPartition.GetFixedWindowLimiter(
|
|
249
|
+
context.User.Identity?.Name ?? context.Connection.RemoteIpAddress?.ToString() ?? "anonymous",
|
|
250
|
+
_ => new FixedWindowRateLimiterOptions
|
|
251
|
+
{
|
|
252
|
+
Window = TimeSpan.FromMinutes(1),
|
|
253
|
+
PermitLimit = 50
|
|
254
|
+
}));
|
|
255
|
+
|
|
256
|
+
options.OnRejected = async (context, token) =>
|
|
257
|
+
{
|
|
258
|
+
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
|
|
259
|
+
await context.HttpContext.Response.WriteAsJsonAsync(new
|
|
260
|
+
{
|
|
261
|
+
error = "Too many requests. Please try again later."
|
|
262
|
+
}, token);
|
|
263
|
+
};
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Usage
|
|
267
|
+
app.UseRateLimiter();
|
|
268
|
+
|
|
269
|
+
app.MapGet("/api/data", () => "Hello")
|
|
270
|
+
.RequireRateLimiting("fixed");
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Secrets Management
|
|
274
|
+
|
|
275
|
+
```csharp
|
|
276
|
+
// Development: User Secrets
|
|
277
|
+
// dotnet user-secrets init
|
|
278
|
+
// dotnet user-secrets set "Database:Password" "secret123"
|
|
279
|
+
|
|
280
|
+
// Production: Azure Key Vault
|
|
281
|
+
builder.Configuration.AddAzureKeyVault(
|
|
282
|
+
new Uri($"https://{builder.Configuration["KeyVault:Name"]}.vault.azure.net/"),
|
|
283
|
+
new DefaultAzureCredential());
|
|
284
|
+
|
|
285
|
+
// Environment variables (Docker/K8s)
|
|
286
|
+
builder.Configuration.AddEnvironmentVariables(prefix: "APP_");
|
|
287
|
+
|
|
288
|
+
// Never do this:
|
|
289
|
+
// ❌ var password = "hardcoded_password";
|
|
290
|
+
// ❌ var connString = "Server=...;Password=secret;";
|
|
291
|
+
|
|
292
|
+
// Always do this:
|
|
293
|
+
// ✅ var password = builder.Configuration["Database:Password"];
|
|
294
|
+
// ✅ var connString = builder.Configuration.GetConnectionString("Default");
|
|
295
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: .NET Tooling
|
|
3
|
+
description: Essential tooling for .NET development and CI/CD.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [dotnet, tooling, testing, build, nuget]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/*.csproj', '**/*.sln', '.editorconfig', 'Directory.Build.props']
|
|
8
|
+
keywords: [PackageReference, dotnet, test, build, publish]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# .NET Tooling
|
|
12
|
+
|
|
13
|
+
## **Priority: P1 (OPERATIONAL)**
|
|
14
|
+
|
|
15
|
+
Essential tooling for .NET development, testing, and CI/CD.
|
|
16
|
+
|
|
17
|
+
## Implementation Guidelines
|
|
18
|
+
|
|
19
|
+
- **Project Files**: SDK-style `.csproj`. Use `Directory.Build.props` for shared settings.
|
|
20
|
+
- **Central Package Management**: `Directory.Packages.props` for version consistency.
|
|
21
|
+
- **Testing**: xUnit or NUnit. FluentAssertions for readable assertions. Moq for mocking.
|
|
22
|
+
- **Code Analysis**: Enable built-in analyzers. Consider StyleCop, SonarQube.
|
|
23
|
+
- **Build**: `dotnet build`, `dotnet publish`. Use Release config for production.
|
|
24
|
+
- **EditorConfig**: Consistent code style across team.
|
|
25
|
+
- **CI/CD**: GitHub Actions, Azure DevOps, or GitLab CI.
|
|
26
|
+
|
|
27
|
+
## Anti-Patterns
|
|
28
|
+
|
|
29
|
+
- **No `packages.config`**: Migrate to `PackageReference`.
|
|
30
|
+
- **No skipping tests in CI**: Tests must pass before merge.
|
|
31
|
+
- **No ignoring warnings**: Fix or suppress with documented justification.
|
|
32
|
+
- **No floating versions**: Pin versions (`8.0.0`, not `8.*`).
|
|
33
|
+
|
|
34
|
+
## Code
|
|
35
|
+
|
|
36
|
+
```xml
|
|
37
|
+
<!-- Directory.Build.props (shared across all projects) -->
|
|
38
|
+
<Project>
|
|
39
|
+
<PropertyGroup>
|
|
40
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
41
|
+
<Nullable>enable</Nullable>
|
|
42
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
43
|
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
|
44
|
+
<AnalysisLevel>latest-recommended</AnalysisLevel>
|
|
45
|
+
</PropertyGroup>
|
|
46
|
+
</Project>
|
|
47
|
+
|
|
48
|
+
<!-- Directory.Packages.props (Central Package Management) -->
|
|
49
|
+
<Project>
|
|
50
|
+
<PropertyGroup>
|
|
51
|
+
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
|
52
|
+
</PropertyGroup>
|
|
53
|
+
<ItemGroup>
|
|
54
|
+
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
|
|
55
|
+
<PackageVersion Include="FluentValidation" Version="11.9.0" />
|
|
56
|
+
<PackageVersion Include="xunit" Version="2.6.6" />
|
|
57
|
+
</ItemGroup>
|
|
58
|
+
</Project>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```csharp
|
|
62
|
+
// xUnit test with FluentAssertions
|
|
63
|
+
public class UserServiceTests
|
|
64
|
+
{
|
|
65
|
+
[Fact]
|
|
66
|
+
public async Task GetUser_WithValidId_ReturnsUser()
|
|
67
|
+
{
|
|
68
|
+
// Arrange
|
|
69
|
+
var mockRepo = new Mock<IUserRepository>();
|
|
70
|
+
mockRepo.Setup(r => r.GetByIdAsync(1, default))
|
|
71
|
+
.ReturnsAsync(new User { Id = 1, Name = "Test" });
|
|
72
|
+
var sut = new UserService(mockRepo.Object);
|
|
73
|
+
|
|
74
|
+
// Act
|
|
75
|
+
var result = await sut.GetUserAsync(1);
|
|
76
|
+
|
|
77
|
+
// Assert
|
|
78
|
+
result.Should().NotBeNull();
|
|
79
|
+
result!.Name.Should().Be("Test");
|
|
80
|
+
mockRepo.Verify(r => r.GetByIdAsync(1, default), Times.Once);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Reference & Examples
|
|
86
|
+
|
|
87
|
+
For CI/CD configuration and Docker setup:
|
|
88
|
+
See [references/REFERENCE.md](references/REFERENCE.md).
|
|
89
|
+
|
|
90
|
+
## Related Topics
|
|
91
|
+
|
|
92
|
+
language | best-practices | security
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# .NET Tooling Reference
|
|
2
|
+
|
|
3
|
+
Testing, CI/CD, and build configuration patterns.
|
|
4
|
+
|
|
5
|
+
## References
|
|
6
|
+
|
|
7
|
+
- [**Testing Setup**](testing-setup.md) - xUnit, NUnit, integration tests.
|
|
8
|
+
- [**CI/CD**](ci-cd.md) - GitHub Actions, Azure DevOps pipelines.
|
|
9
|
+
- [**Docker**](docker.md) - Multi-stage builds for .NET.
|
|
10
|
+
|
|
11
|
+
## Project File (.csproj) Reference
|
|
12
|
+
|
|
13
|
+
```xml
|
|
14
|
+
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
15
|
+
<PropertyGroup>
|
|
16
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
17
|
+
<Nullable>enable</Nullable>
|
|
18
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
19
|
+
|
|
20
|
+
<!-- Code analysis -->
|
|
21
|
+
<AnalysisLevel>latest-recommended</AnalysisLevel>
|
|
22
|
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
|
23
|
+
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
|
24
|
+
|
|
25
|
+
<!-- Assembly info -->
|
|
26
|
+
<Version>1.0.0</Version>
|
|
27
|
+
<Authors>Your Name</Authors>
|
|
28
|
+
|
|
29
|
+
<!-- Build optimization -->
|
|
30
|
+
<PublishTrimmed>true</PublishTrimmed>
|
|
31
|
+
<PublishSingleFile>true</PublishSingleFile>
|
|
32
|
+
<SelfContained>true</SelfContained>
|
|
33
|
+
</PropertyGroup>
|
|
34
|
+
|
|
35
|
+
<ItemGroup>
|
|
36
|
+
<!-- Package references (versions from Directory.Packages.props) -->
|
|
37
|
+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
|
|
38
|
+
<PackageReference Include="Serilog.AspNetCore" />
|
|
39
|
+
</ItemGroup>
|
|
40
|
+
|
|
41
|
+
<ItemGroup>
|
|
42
|
+
<!-- Project references -->
|
|
43
|
+
<ProjectReference Include="..\Domain\Domain.csproj" />
|
|
44
|
+
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
|
45
|
+
</ItemGroup>
|
|
46
|
+
</Project>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## GitHub Actions CI/CD
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
# .github/workflows/ci.yml
|
|
53
|
+
name: CI/CD
|
|
54
|
+
|
|
55
|
+
on:
|
|
56
|
+
push:
|
|
57
|
+
branches: [main, develop]
|
|
58
|
+
pull_request:
|
|
59
|
+
branches: [main]
|
|
60
|
+
|
|
61
|
+
env:
|
|
62
|
+
DOTNET_VERSION: '8.0.x'
|
|
63
|
+
|
|
64
|
+
jobs:
|
|
65
|
+
build-and-test:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v4
|
|
69
|
+
|
|
70
|
+
- name: Setup .NET
|
|
71
|
+
uses: actions/setup-dotnet@v4
|
|
72
|
+
with:
|
|
73
|
+
dotnet-version: ${{ env.DOTNET_VERSION }}
|
|
74
|
+
|
|
75
|
+
- name: Restore dependencies
|
|
76
|
+
run: dotnet restore
|
|
77
|
+
|
|
78
|
+
- name: Build
|
|
79
|
+
run: dotnet build --no-restore --configuration Release
|
|
80
|
+
|
|
81
|
+
- name: Test
|
|
82
|
+
run: dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" --results-directory ./coverage
|
|
83
|
+
|
|
84
|
+
- name: Upload coverage
|
|
85
|
+
uses: codecov/codecov-action@v3
|
|
86
|
+
with:
|
|
87
|
+
directory: ./coverage
|
|
88
|
+
fail_ci_if_error: true
|
|
89
|
+
|
|
90
|
+
publish:
|
|
91
|
+
needs: build-and-test
|
|
92
|
+
if: github.ref == 'refs/heads/main'
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
steps:
|
|
95
|
+
- uses: actions/checkout@v4
|
|
96
|
+
|
|
97
|
+
- name: Setup .NET
|
|
98
|
+
uses: actions/setup-dotnet@v4
|
|
99
|
+
with:
|
|
100
|
+
dotnet-version: ${{ env.DOTNET_VERSION }}
|
|
101
|
+
|
|
102
|
+
- name: Publish
|
|
103
|
+
run: dotnet publish src/WebApi/WebApi.csproj -c Release -o ./publish
|
|
104
|
+
|
|
105
|
+
- name: Build and push Docker image
|
|
106
|
+
uses: docker/build-push-action@v5
|
|
107
|
+
with:
|
|
108
|
+
context: .
|
|
109
|
+
push: true
|
|
110
|
+
tags: ghcr.io/${{ github.repository }}:latest
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Docker Multi-Stage Build
|
|
114
|
+
|
|
115
|
+
```dockerfile
|
|
116
|
+
# Dockerfile
|
|
117
|
+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
|
118
|
+
WORKDIR /src
|
|
119
|
+
|
|
120
|
+
# Copy csproj files and restore
|
|
121
|
+
COPY ["src/WebApi/WebApi.csproj", "src/WebApi/"]
|
|
122
|
+
COPY ["src/Domain/Domain.csproj", "src/Domain/"]
|
|
123
|
+
COPY ["src/Infrastructure/Infrastructure.csproj", "src/Infrastructure/"]
|
|
124
|
+
COPY ["Directory.Build.props", "./"]
|
|
125
|
+
COPY ["Directory.Packages.props", "./"]
|
|
126
|
+
RUN dotnet restore "src/WebApi/WebApi.csproj"
|
|
127
|
+
|
|
128
|
+
# Copy everything and build
|
|
129
|
+
COPY . .
|
|
130
|
+
RUN dotnet publish "src/WebApi/WebApi.csproj" -c Release -o /app/publish --no-restore
|
|
131
|
+
|
|
132
|
+
# Runtime image
|
|
133
|
+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
|
|
134
|
+
WORKDIR /app
|
|
135
|
+
|
|
136
|
+
# Create non-root user
|
|
137
|
+
RUN adduser --disabled-password --gecos "" appuser
|
|
138
|
+
USER appuser
|
|
139
|
+
|
|
140
|
+
COPY --from=build /app/publish .
|
|
141
|
+
|
|
142
|
+
ENV ASPNETCORE_URLS=http://+:8080
|
|
143
|
+
EXPOSE 8080
|
|
144
|
+
|
|
145
|
+
ENTRYPOINT ["dotnet", "WebApi.dll"]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Integration Testing with WebApplicationFactory
|
|
149
|
+
|
|
150
|
+
```csharp
|
|
151
|
+
// CustomWebApplicationFactory.cs
|
|
152
|
+
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram>
|
|
153
|
+
where TProgram : class
|
|
154
|
+
{
|
|
155
|
+
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
|
156
|
+
{
|
|
157
|
+
builder.ConfigureServices(services =>
|
|
158
|
+
{
|
|
159
|
+
// Replace real database with in-memory
|
|
160
|
+
var descriptor = services.SingleOrDefault(
|
|
161
|
+
d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
|
|
162
|
+
|
|
163
|
+
if (descriptor != null)
|
|
164
|
+
services.Remove(descriptor);
|
|
165
|
+
|
|
166
|
+
services.AddDbContext<AppDbContext>(options =>
|
|
167
|
+
options.UseInMemoryDatabase("TestDb"));
|
|
168
|
+
|
|
169
|
+
// Replace external services with fakes
|
|
170
|
+
services.AddScoped<IEmailService, FakeEmailService>();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
builder.UseEnvironment("Testing");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Integration tests
|
|
178
|
+
public class UsersApiTests : IClassFixture<CustomWebApplicationFactory<Program>>
|
|
179
|
+
{
|
|
180
|
+
private readonly HttpClient _client;
|
|
181
|
+
private readonly CustomWebApplicationFactory<Program> _factory;
|
|
182
|
+
|
|
183
|
+
public UsersApiTests(CustomWebApplicationFactory<Program> factory)
|
|
184
|
+
{
|
|
185
|
+
_factory = factory;
|
|
186
|
+
_client = factory.CreateClient();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
[Fact]
|
|
190
|
+
public async Task GetUsers_ReturnsSuccessAndUsers()
|
|
191
|
+
{
|
|
192
|
+
// Arrange - seed data
|
|
193
|
+
using var scope = _factory.Services.CreateScope();
|
|
194
|
+
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
195
|
+
db.Users.Add(new User { Name = "Test User", Email = "test@example.com" });
|
|
196
|
+
await db.SaveChangesAsync();
|
|
197
|
+
|
|
198
|
+
// Act
|
|
199
|
+
var response = await _client.GetAsync("/api/users");
|
|
200
|
+
|
|
201
|
+
// Assert
|
|
202
|
+
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
203
|
+
var users = await response.Content.ReadFromJsonAsync<List<UserDto>>();
|
|
204
|
+
users.Should().ContainSingle(u => u.Email == "test@example.com");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
[Fact]
|
|
208
|
+
public async Task CreateUser_WithValidData_ReturnsCreated()
|
|
209
|
+
{
|
|
210
|
+
// Arrange
|
|
211
|
+
var newUser = new CreateUserDto { Name = "New User", Email = "new@example.com" };
|
|
212
|
+
|
|
213
|
+
// Act
|
|
214
|
+
var response = await _client.PostAsJsonAsync("/api/users", newUser);
|
|
215
|
+
|
|
216
|
+
// Assert
|
|
217
|
+
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
|
218
|
+
response.Headers.Location.Should().NotBeNull();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## EditorConfig
|
|
224
|
+
|
|
225
|
+
```ini
|
|
226
|
+
# .editorconfig
|
|
227
|
+
root = true
|
|
228
|
+
|
|
229
|
+
[*]
|
|
230
|
+
indent_style = space
|
|
231
|
+
indent_size = 4
|
|
232
|
+
end_of_line = lf
|
|
233
|
+
charset = utf-8
|
|
234
|
+
trim_trailing_whitespace = true
|
|
235
|
+
insert_final_newline = true
|
|
236
|
+
|
|
237
|
+
[*.cs]
|
|
238
|
+
# Naming conventions
|
|
239
|
+
dotnet_naming_rule.private_fields_should_be_camel_case.severity = error
|
|
240
|
+
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
|
|
241
|
+
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_underscore
|
|
242
|
+
|
|
243
|
+
dotnet_naming_symbols.private_fields.applicable_kinds = field
|
|
244
|
+
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
|
|
245
|
+
|
|
246
|
+
dotnet_naming_style.camel_case_underscore.required_prefix = _
|
|
247
|
+
dotnet_naming_style.camel_case_underscore.capitalization = camel_case
|
|
248
|
+
|
|
249
|
+
# Code style
|
|
250
|
+
csharp_style_var_for_built_in_types = false:suggestion
|
|
251
|
+
csharp_style_var_when_type_is_apparent = true:suggestion
|
|
252
|
+
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
|
|
253
|
+
csharp_prefer_braces = true:warning
|
|
254
|
+
|
|
255
|
+
# Formatting
|
|
256
|
+
csharp_new_line_before_open_brace = all
|
|
257
|
+
csharp_new_line_before_else = true
|
|
258
|
+
csharp_new_line_before_catch = true
|
|
259
|
+
csharp_new_line_before_finally = true
|
|
260
|
+
|
|
261
|
+
[*.{json,yml,yaml}]
|
|
262
|
+
indent_size = 2
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Benchmark.NET Setup
|
|
266
|
+
|
|
267
|
+
```csharp
|
|
268
|
+
// Install: dotnet add package BenchmarkDotNet
|
|
269
|
+
|
|
270
|
+
[MemoryDiagnoser]
|
|
271
|
+
[SimpleJob(RuntimeMoniker.Net80)]
|
|
272
|
+
public class StringConcatBenchmarks
|
|
273
|
+
{
|
|
274
|
+
private readonly string[] _strings = Enumerable.Range(0, 100)
|
|
275
|
+
.Select(i => $"String{i}").ToArray();
|
|
276
|
+
|
|
277
|
+
[Benchmark(Baseline = true)]
|
|
278
|
+
public string ConcatWithPlus()
|
|
279
|
+
{
|
|
280
|
+
string result = "";
|
|
281
|
+
foreach (var s in _strings)
|
|
282
|
+
result += s;
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
[Benchmark]
|
|
287
|
+
public string ConcatWithStringBuilder()
|
|
288
|
+
{
|
|
289
|
+
var sb = new StringBuilder();
|
|
290
|
+
foreach (var s in _strings)
|
|
291
|
+
sb.Append(s);
|
|
292
|
+
return sb.ToString();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
[Benchmark]
|
|
296
|
+
public string ConcatWithJoin() => string.Join("", _strings);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Run: dotnet run -c Release
|
|
300
|
+
```
|