diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5e02d448..401887bb 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -24,22 +24,21 @@
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 83a864c0..ba5dcc4d 100644
--- a/README.md
+++ b/README.md
@@ -33,64 +33,8 @@ It showcases:
1. Learn more about [dotnet-ef](https://learn.microsoft.com/en-us/ef/core/cli/dotnet)
### Running the application
-To run the application, run both the [Todo.Web/Server](Todo.Web/Server) and [TodoApi](TodoApi). Below are different ways to run both applications:
- - **Visual Studio** - Setup multiple startup projects by right clicking on the solution and selecting Properties. Select `TodoApi` and `Todo.Web.Server` as startup projects.
-
-
-
- - **Visual Studio Code** - A `compound` launch definition is provided in the `launch.json` file, use the 'Run and Debug' view and select 'Web and API' and start that profile.
-
- ![image](https://github.com/timheuer/TodoApi/assets/4821/dfa83682-86c4-45ae-965b-fa6f6a6f07a9)
-
-
- - **Terminal/CLI** - Open up 2 terminal windows, one in [Todo.Web.Server](Todo.Web/Server/) and the other in [TodoApi](TodoApi/) run:
-
- ```
- dotnet watch run -lp https
- ```
-
- This will run both applications with the `https` profile.
-
- - **Tye** - Install the global tool using the following command:
-
- ```
- dotnet tool install --global Microsoft.Tye --version 0.11.0-alpha.22111.1
- ```
-
- Run `tye run` in the repository root and navigate to the tye dashboard (usually http://localhost:8000) to see both applications running.
-
- - **Docker Compose** - Open your terminal, navigate to the root folder of this project and run the following commands:
- 1. Build a docker image for the `TodoApi` directly from dotnet publish.
- ```
- dotnet publish ./TodoApi/TodoApi.csproj --os linux --arch x64 /t:PublishContainer -c Release
- ```
-
- 1. Build a docker image for the `Todo.Web.Server` directly from dotnet publish.
- ```
- dotnet publish ./Todo.Web/Server/Todo.Web.Server.csproj --os linux --arch x64 /t:PublishContainer -c Release --self-contained true
- ```
-
- 1. Generate certificate and configure local machine so we can start our apps with https support using docker compose.
-
- Windows using Linux containers
- ```
- set PASSWORD YourPasswordHere
- dotnet dev-certs https -ep ${HOME}/.aspnet/https/todoapps.pfx -p $PASSWORD --trust
- ```
- macOS or Linux
- ```
- export PASSWORD=YourPasswordHere
- dotnet dev-certs https -ep ~/.aspnet/https/todoapps.pfx -p $PASSWORD --trust
- ```
-
- 1. Change these variables below in the `docker-compose.yml` file to match your https certificate and password.
- - `ASPNETCORE_Kestrel__Certificates__Default__Password`
- - `ASPNETCORE_Kestrel__Certificates__Default__Path`
-
- 1. Run `docker-compose up -d` to spin up both apps todo-api and todo-web-server plus jaeger and prometheus.
-
- 1. Navigate to the Todo Web app https://localhost:5003.
+To run the application, run the [TodoApi.AppHost](TodoApi.AppHost) project. This uses .NET Aspire to run both the [Todo.Web/Server](Todo.Web/Server) and [TodoApi](TodoApi).
## Optional
@@ -188,26 +132,6 @@ This sample has **Auth0** configured as an OIDC server. It can be configured wit
Learn more about the Auth0 .NET SDK [here](https://github.com/auth0/auth0-aspnetcore-authentication).
### OpenTelemetry
-TodoApi uses OpenTelemetry to collect logs, metrics and spans.
-
-If you wish to view the collected telemetry, follow the steps below.
-
-#### Metrics
-1. Run Prometheus with Docker:
-```
-docker run -d -p 9090:9090 --name prometheus -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
-```
-1. Open [Prometheus in your browser](http://localhost:9090/)
-1. Query the collected metrics
-
-#### Spans
-
-1. Configure environment variable `OTEL_EXPORTER_OTLP_ENDPOINT` with the right endpoint URL to enable `.AddOtlpExporter` below `builder.Services.AddOpenTelemetryTracing`, in the `TodoApi/OpenTelemetryExtensions.cs` file
-1. Run Jaeger with Docker:
-
-```
-docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 -e COLLECTOR_OTLP_ENABLED=true -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest
-```
-1. Open [Jaeger in your browser](http://localhost:16686/)
-1. View the collected spans
+TodoApi uses OpenTelemetry to collect logs, metrics and spans. You can see this
+using the [Aspire Dashboard](https://aspiredashboard.com/).
diff --git a/Todo.Web/Server/AuthApi.cs b/Todo.Web/Server/AuthApi.cs
index 2f6480c2..05b0939c 100644
--- a/Todo.Web/Server/AuthApi.cs
+++ b/Todo.Web/Server/AuthApi.cs
@@ -57,7 +57,7 @@ public static RouteGroupBuilder MapAuth(this IEndpointRouteBuilder routes)
// This name maps to the registered authentication scheme names in AuthenticationExtensions.cs
return Results.Challenge(
properties: new() { RedirectUri = $"/auth/signin/{provider}" },
- authenticationSchemes: new[] { provider });
+ authenticationSchemes: [provider]);
});
group.MapGet("signin/{provider}", async (string provider, AuthClient client, HttpContext context) =>
diff --git a/Todo.Web/Server/Program.cs b/Todo.Web/Server/Program.cs
index 776e903a..2825e92a 100644
--- a/Todo.Web/Server/Program.cs
+++ b/Todo.Web/Server/Program.cs
@@ -3,6 +3,8 @@
var builder = WebApplication.CreateBuilder(args);
+builder.AddServiceDefaults();
+
// Configure auth with the front end
builder.AddAuthentication();
builder.Services.AddAuthorizationBuilder();
@@ -14,20 +16,18 @@
.AddInteractiveWebAssemblyComponents();
// Add the forwarder to make sending requests to the backend easier
-builder.Services.AddHttpForwarder();
-
-var todoUrl = builder.Configuration.GetServiceUri("todoapi")?.ToString() ??
- builder.Configuration["TodoApiUrl"] ??
- throw new InvalidOperationException("Todo API URL is not configured");
+builder.Services.AddHttpForwarderWithServiceDiscovery();
// Configure the HttpClient for the backend API
builder.Services.AddHttpClient(client =>
{
- client.BaseAddress = new(todoUrl);
+ client.BaseAddress = new("http://todoapi");
});
var app = builder.Build();
+app.MapDefaultEndpoints();
+
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
@@ -48,7 +48,7 @@
// Configure the APIs
app.MapAuth();
-app.MapTodos(todoUrl);
+app.MapTodos();
app.Run();
diff --git a/Todo.Web/Server/Todo.Web.Server.csproj b/Todo.Web/Server/Todo.Web.Server.csproj
index cbe9f771..7ba10bdd 100644
--- a/Todo.Web/Server/Todo.Web.Server.csproj
+++ b/Todo.Web/Server/Todo.Web.Server.csproj
@@ -14,10 +14,11 @@
-
+
+
diff --git a/Todo.Web/Server/TodoApi.cs b/Todo.Web/Server/TodoApi.cs
index 13304884..5fa74ca4 100644
--- a/Todo.Web/Server/TodoApi.cs
+++ b/Todo.Web/Server/TodoApi.cs
@@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Authentication;
using Yarp.ReverseProxy.Forwarder;
using Yarp.ReverseProxy.Transforms;
-using Yarp.ReverseProxy.Transforms.Builder;
namespace Todo.Web.Server;
public static class TodoApi
{
- public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes, string todoUrl)
+ public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes)
{
// The todo API translates the authentication cookie between the browser the BFF into an
// access token that is sent to the todo API. We're using YARP to forward the request.
@@ -16,7 +15,7 @@ public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes, stri
group.RequireAuthorization();
- group.MapForwarder("{*path}", todoUrl, new ForwarderRequestConfig(), b =>
+ group.MapForwarder("{*path}", "http://todoapi", new ForwarderRequestConfig(), b =>
{
b.AddRequestTransform(async c =>
{
diff --git a/TodoApi.AppHost/Program.cs b/TodoApi.AppHost/Program.cs
new file mode 100644
index 00000000..f8fc8c81
--- /dev/null
+++ b/TodoApi.AppHost/Program.cs
@@ -0,0 +1,8 @@
+var builder = DistributedApplication.CreateBuilder(args);
+
+var todoapi = builder.AddProject("todoapi");
+
+builder.AddProject("todo-web-server")
+ .WithReference(todoapi);
+
+builder.Build().Run();
diff --git a/TodoApi.AppHost/Properties/launchSettings.json b/TodoApi.AppHost/Properties/launchSettings.json
new file mode 100644
index 00000000..45004599
--- /dev/null
+++ b/TodoApi.AppHost/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:17249;http://localhost:15169",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21226",
+ "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22195"
+ }
+ },
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:15169",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19224",
+ "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20024"
+ }
+ }
+ }
+}
diff --git a/TodoApi.AppHost/TodoApi.AppHost.csproj b/TodoApi.AppHost/TodoApi.AppHost.csproj
new file mode 100644
index 00000000..ced1d6ec
--- /dev/null
+++ b/TodoApi.AppHost/TodoApi.AppHost.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ true
+ 22f8485f-c345-41fa-9f21-ba96b1e511d1
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TodoApi.AppHost/appsettings.Development.json b/TodoApi.AppHost/appsettings.Development.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/TodoApi.AppHost/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/TodoApi.AppHost/appsettings.json b/TodoApi.AppHost/appsettings.json
new file mode 100644
index 00000000..31c092aa
--- /dev/null
+++ b/TodoApi.AppHost/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning",
+ "Aspire.Hosting.Dcp": "Warning"
+ }
+ }
+}
diff --git a/TodoApi.ServiceDefaults/Extensions.cs b/TodoApi.ServiceDefaults/Extensions.cs
new file mode 100644
index 00000000..13151bf4
--- /dev/null
+++ b/TodoApi.ServiceDefaults/Extensions.cs
@@ -0,0 +1,119 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.ServiceDiscovery;
+using OpenTelemetry;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
+
+namespace Microsoft.Extensions.Hosting;
+
+// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
+// This project should be referenced by each service project in your solution.
+// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
+public static class Extensions
+{
+ public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder
+ {
+ builder.ConfigureOpenTelemetry();
+
+ builder.AddDefaultHealthChecks();
+
+ builder.Services.AddServiceDiscovery();
+
+ builder.Services.ConfigureHttpClientDefaults(http =>
+ {
+ // Turn on resilience by default
+ http.AddStandardResilienceHandler();
+
+ // Turn on service discovery by default
+ http.AddServiceDiscovery();
+ });
+
+ // Uncomment the following to restrict the allowed schemes for service discovery.
+ // builder.Services.Configure(options =>
+ // {
+ // options.AllowedSchemes = ["https"];
+ // });
+
+ return builder;
+ }
+
+ public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder
+ {
+ builder.Logging.AddOpenTelemetry(logging =>
+ {
+ logging.IncludeFormattedMessage = true;
+ logging.IncludeScopes = true;
+ });
+
+ builder.Services.AddOpenTelemetry()
+ .WithMetrics(metrics =>
+ {
+ metrics.AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddRuntimeInstrumentation();
+ })
+ .WithTracing(tracing =>
+ {
+ tracing.AddSource(builder.Environment.ApplicationName)
+ .AddAspNetCoreInstrumentation()
+ // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
+ //.AddGrpcClientInstrumentation()
+ .AddHttpClientInstrumentation();
+ });
+
+ builder.AddOpenTelemetryExporters();
+
+ return builder;
+ }
+
+ private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder
+ {
+ var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
+
+ if (useOtlpExporter)
+ {
+ builder.Services.AddOpenTelemetry().UseOtlpExporter();
+ }
+
+ // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
+ //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
+ //{
+ // builder.Services.AddOpenTelemetry()
+ // .UseAzureMonitor();
+ //}
+
+ return builder;
+ }
+
+ public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder
+ {
+ builder.Services.AddHealthChecks()
+ // Add a default liveness check to ensure app is responsive
+ .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
+
+ return builder;
+ }
+
+ public static WebApplication MapDefaultEndpoints(this WebApplication app)
+ {
+ // Adding health checks endpoints to applications in non-development environments has security implications.
+ // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
+ if (app.Environment.IsDevelopment())
+ {
+ // All health checks must pass for app to be considered ready to accept traffic after starting
+ app.MapHealthChecks("/health");
+
+ // Only health checks tagged with the "live" tag must pass for app to be considered alive
+ app.MapHealthChecks("/alive", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("live")
+ });
+ }
+
+ return app;
+ }
+}
diff --git a/TodoApi.ServiceDefaults/TodoApi.ServiceDefaults.csproj b/TodoApi.ServiceDefaults/TodoApi.ServiceDefaults.csproj
new file mode 100644
index 00000000..4f74b4fb
--- /dev/null
+++ b/TodoApi.ServiceDefaults/TodoApi.ServiceDefaults.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TodoApi.sln b/TodoApi.sln
index 9fbd068f..fc505a41 100644
--- a/TodoApi.sln
+++ b/TodoApi.sln
@@ -11,9 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
- docker-compose.yml = docker-compose.yml
README.md = README.md
- tye.yaml = tye.yaml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo.Web.Server", "Todo.Web\Server\Todo.Web.Server.csproj", "{9459B25A-3A37-43C4-A7DE-6113C1E2CDD7}"
@@ -22,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo.Web.Client", "Todo.Web
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo.Web.Shared", "Todo.Web\Shared\Todo.Web.Shared.csproj", "{272942F6-94E8-4D6B-8AD8-C4CCA305836D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApi.AppHost", "TodoApi.AppHost\TodoApi.AppHost.csproj", "{4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApi.ServiceDefaults", "TodoApi.ServiceDefaults\TodoApi.ServiceDefaults.csproj", "{67B571B3-F4FB-46AA-888E-17A5A773610C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,6 +50,14 @@ Global
{272942F6-94E8-4D6B-8AD8-C4CCA305836D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{272942F6-94E8-4D6B-8AD8-C4CCA305836D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{272942F6-94E8-4D6B-8AD8-C4CCA305836D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67B571B3-F4FB-46AA-888E-17A5A773610C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67B571B3-F4FB-46AA-888E-17A5A773610C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67B571B3-F4FB-46AA-888E-17A5A773610C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67B571B3-F4FB-46AA-888E-17A5A773610C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/TodoApi/Extensions/OpenApiExtensions.cs b/TodoApi/Extensions/OpenApiExtensions.cs
index c997e08b..2d544bd9 100644
--- a/TodoApi/Extensions/OpenApiExtensions.cs
+++ b/TodoApi/Extensions/OpenApiExtensions.cs
@@ -32,7 +32,7 @@ public static IEndpointConventionBuilder AddOpenApiSecurity(this IEndpointConven
{
new()
{
- [BearerScheme] = Array.Empty()
+ [BearerScheme] = []
}
}
});
diff --git a/TodoApi/Extensions/OpenTelemetryExtensions.cs b/TodoApi/Extensions/OpenTelemetryExtensions.cs
deleted file mode 100644
index c78a4265..00000000
--- a/TodoApi/Extensions/OpenTelemetryExtensions.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using OpenTelemetry;
-using OpenTelemetry.Logs;
-using OpenTelemetry.Metrics;
-using OpenTelemetry.Resources;
-using OpenTelemetry.Trace;
-
-namespace TodoApi;
-
-public static class OpenTelemetryExtensions
-{
-
- ///
- /// Configures logging, distributed tracing, and metrics
- ///
- /// - Distributed tracing uses the OTLP Exporter, which can be viewed with Jaeger
- /// - Metrics uses the Prometheus Exporter
- /// - Logging can use the OTLP Exporter, but due to limited vendor support it is not enabled by default
- ///
- ///
- ///
- ///
- public static WebApplicationBuilder AddOpenTelemetry(this WebApplicationBuilder builder)
- {
- var resourceBuilder = ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName);
- var otlpEndpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
-
- if (!string.IsNullOrWhiteSpace(otlpEndpoint))
- {
- builder.Logging.AddOpenTelemetry(logging =>
- {
- logging.SetResourceBuilder(resourceBuilder)
- .AddOtlpExporter();
- });
- }
-
- builder.Services.AddOpenTelemetry()
- .WithMetrics(metrics =>
- {
- metrics.SetResourceBuilder(resourceBuilder)
- .AddPrometheusExporter()
- .AddAspNetCoreInstrumentation()
- .AddRuntimeInstrumentation()
- .AddHttpClientInstrumentation()
- .AddEventCountersInstrumentation(c =>
- {
- // https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters
- c.AddEventSources(
- "Microsoft.AspNetCore.Hosting",
- "Microsoft-AspNetCore-Server-Kestrel",
- "System.Net.Http",
- "System.Net.Sockets",
- "System.Net.NameResolution",
- "System.Net.Security");
- });
- })
- .WithTracing(tracing =>
- {
- // We need to use AlwaysSampler to record spans
- // from Todo.Web.Server, because there it no OpenTelemetry
- // instrumentation
- tracing.SetResourceBuilder(resourceBuilder)
- .SetSampler(new AlwaysOnSampler())
- .AddAspNetCoreInstrumentation()
- .AddHttpClientInstrumentation()
- .AddEntityFrameworkCoreInstrumentation();
-
- if (!string.IsNullOrWhiteSpace(otlpEndpoint))
- {
- tracing.AddOtlpExporter();
- }
- });
-
- return builder;
- }
-}
diff --git a/TodoApi/Filters/ValidationFilter.cs b/TodoApi/Filters/ValidationFilter.cs
index a12ba476..92db9db1 100644
--- a/TodoApi/Filters/ValidationFilter.cs
+++ b/TodoApi/Filters/ValidationFilter.cs
@@ -23,7 +23,7 @@ public static TBuilder WithParameterValidation(this TBuilder builder,
{
if (typesToValidate.Contains(p.ParameterType))
{
- parameterIndexesToValidate ??= new();
+ parameterIndexesToValidate ??= [];
parameterIndexesToValidate.Add(p.Position);
}
}
@@ -58,17 +58,11 @@ public static TBuilder WithParameterValidation(this TBuilder builder,
}
// Equivalent to the .Produces call to add metadata to endpoints
- private sealed class ProducesResponseTypeMetadata : IProducesResponseTypeMetadata
+ private sealed class ProducesResponseTypeMetadata(Type type, int statusCode, string contentType) :
+ IProducesResponseTypeMetadata
{
- public ProducesResponseTypeMetadata(Type type, int statusCode, string contentType)
- {
- Type = type;
- StatusCode = statusCode;
- ContentTypes = new[] { contentType };
- }
-
- public Type Type { get; }
- public int StatusCode { get; }
- public IEnumerable ContentTypes { get; }
+ public Type Type { get; } = type;
+ public int StatusCode { get; } = statusCode;
+ public IEnumerable ContentTypes { get; } = [contentType];
}
}
diff --git a/TodoApi/Program.cs b/TodoApi/Program.cs
index 8b7b150a..a8aed1c8 100644
--- a/TodoApi/Program.cs
+++ b/TodoApi/Program.cs
@@ -1,7 +1,10 @@
+using Microsoft.AspNetCore.HttpLogging;
using Microsoft.AspNetCore.Identity;
var builder = WebApplication.CreateBuilder(args);
+builder.AddServiceDefaults();
+
// Configure auth
builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);
builder.Services.AddAuthorizationBuilder().AddCurrentUserHandler();
@@ -25,18 +28,27 @@
// Configure rate limiting
builder.Services.AddRateLimiting();
-// Configure OpenTelemetry
-builder.AddOpenTelemetry();
+builder.Services.AddHttpLogging(o =>
+{
+ if (builder.Environment.IsDevelopment())
+ {
+ o.CombineLogs = true;
+ o.LoggingFields = HttpLoggingFields.ResponseBody | HttpLoggingFields.ResponseHeaders;
+ }
+});
var app = builder.Build();
+app.UseHttpLogging();
+app.UseRateLimiter();
+
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
-app.UseRateLimiter();
+app.MapDefaultEndpoints();
app.Map("/", () => Results.Redirect("/swagger"));
@@ -44,9 +56,4 @@
app.MapTodos();
app.MapUsers();
-// Configure the prometheus endpoint for scraping metrics
-app.MapPrometheusScrapingEndpoint();
-// NOTE: This should only be exposed on an internal port!
-// .RequireHost("*:9100");
-
app.Run();
diff --git a/TodoApi/TodoApi.csproj b/TodoApi/TodoApi.csproj
index 828b0048..47c3c8eb 100644
--- a/TodoApi/TodoApi.csproj
+++ b/TodoApi/TodoApi.csproj
@@ -15,22 +15,14 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/TodoApi/appsettings.Development.json b/TodoApi/appsettings.Development.json
index 4b5aad0f..4b94cfa3 100644
--- a/TodoApi/appsettings.Development.json
+++ b/TodoApi/appsettings.Development.json
@@ -1,11 +1,8 @@
{
- "Serilog": {
- "MinimumLevel": {
+ "Logging": {
+ "LogLevel": {
"Default": "Information",
- "Override": {
- "Microsoft": "Information",
- "Microsoft.Hosting.Lifetime": "Information"
- }
+ "Microsoft.AspNetCore": "Information"
}
},
"Authentication": {
diff --git a/TodoApi/prometheus.yml b/TodoApi/prometheus.yml
deleted file mode 100644
index 3ff39af5..00000000
--- a/TodoApi/prometheus.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-global:
- scrape_interval: 15s
- evaluation_interval: 15s
-
-scrape_configs:
- - job_name: "prometheus"
- static_configs:
- - targets: ["localhost:9090"]
-
- - job_name: "todo-api"
- static_configs:
- - targets: ["host.docker.internal:5000"]
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
deleted file mode 100644
index 45b52658..00000000
--- a/docker-compose.yml
+++ /dev/null
@@ -1,113 +0,0 @@
-version: '3.4'
-
-configs:
- prometheus_config:
- file: ./TodoApi/prometheus.yml
-
-x-common-variables: &common-variables
- ASPNETCORE_ENVIRONMENT: Development
- ASPNETCORE_URLS: https://+:443;http://+:80
- ASPNETCORE_Kestrel__Certificates__Default__Password: YourPasswordHere
- ASPNETCORE_Kestrel__Certificates__Default__Path: /https/todoapps.pfx
-
-services:
- todo-api:
- image: todo-api
- pull_policy: if_not_present
- environment:
- <<: *common-variables
- OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
- ports:
- - "5000:80"
- - "5001:443"
- volumes:
- - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
- - ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
- - ~/.aspnet/https:/https:ro
- - ./TodoApi/.db:/app/.db
- container_name: todo-api
-
- todo-web-server:
- image: todo-web-server
- pull_policy: if_not_present
- environment:
- <<: *common-variables
- TodoApiUrl: http://todo-api
- ports:
- - "5002:80"
- - "5003:443"
- volumes:
- - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
- - ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
- - ~/.aspnet/https:/https:ro
- container_name: todo-web-server
-
- prometheus:
- image: prom/prometheus
- ports:
- - "9090:9090"
- configs:
- - source: prometheus_config
- target: /etc/prometheus/prometheus.yml
- container_name: prometheus
-
- jaeger:
- image: jaegertracing/all-in-one:latest
- environment:
- - COLLECTOR_ZIPKIN_HOST_PORT=:9411
- - COLLECTOR_OTLP_ENABLED=true
- - METRICS_STORAGE_TYPE=prometheus
- - PROMETHEUS_SERVER_URL=http://prometheus:9090
- ports:
- - "6831:6831/udp"
- - "6832:6832/udp"
- - "5778:5778"
- - "16686:16686"
- - "4317:4317"
- - "4318:4318"
- - "14250:14250"
- - "14268:14268"
- - "14269:14269"
- - "9411:9411"
- container_name: jaeger
-
- seq:
- image: datalust/seq:latest
- container_name: seq
- restart: on-failure
- ports:
- - 8081:80
- - 5341:5341
- environment:
- ACCEPT_EULA: Y
-
- elasticsearch:
- container_name: elastic_search
- restart: on-failure
- image: docker.elastic.co/elasticsearch/elasticsearch:latest
- environment:
- - discovery.type=single-node
- - bootstrap.memory_lock=true
- - xpack.monitoring.enabled=true
- - xpack.watcher.enabled=false
- - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- ulimits:
- memlock:
- soft: -1
- hard: -1
- volumes:
- - elastic-data:/usr/share/elasticsearch/data
- ports:
- - "9200:9200"
- - "9300:9300"
-
- kibana:
- image: docker.elastic.co/kibana/kibana:latest
- container_name: kibana
- restart: on-failure
- environment:
- - ELASTICSEARCH_HOSTS=http://elastic_search:9200
- ports:
- - "5601:5601"
- depends_on:
- - elasticsearch
diff --git a/tye.yaml b/tye.yaml
deleted file mode 100644
index 27d7653f..00000000
--- a/tye.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-# tye application configuration file
-# read all about it at https://github.com/dotnet/tye
-#
-# when you've given us a try, we'd love to know what you think:
-# https://aka.ms/AA7q20u
-#
-name: todoapi
-services:
-- name: todoapi
- project: TodoApi/TodoApi.csproj
-- name: todo-web-server
- project: Todo.Web/Server/Todo.Web.Server.csproj