Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
seungyongshim committed Feb 18, 2024
1 parent a0cc239 commit eb7201d
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 77 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,18 @@ builder.Services.ConfigureHttpJsonOptions(options => {
};
});
```

## ProblemDetails에 TraceId 추가하기
* https://stackoverflow.com/questions/76669903/problemdetails-response-does-not-contain-traceid-property
```csharp
builder.Services.AddProblemDetails(options =>
options.CustomizeProblemDetails = (context) =>
{
if (!context.ProblemDetails.Extensions.ContainsKey("traceId"))
{
string? traceId = Activity.Current?.Id ?? context.HttpContext.TraceIdentifier;
context.ProblemDetails.Extensions.Add(new KeyValuePair<string, object?>("traceId", traceId));
}
}
);
```
Original file line number Diff line number Diff line change
@@ -1,35 +1,64 @@
using System.Diagnostics;
using System.Text.Json.Nodes;
using System.Text.Json;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Http.Json;
using System.Net.Mime;

namespace WebApplicationMinimalApi8.EndpointFilters;

public class ResponseAddTraceIdFilter : IEndpointFilter
{
public static JsonNode AddTraceId(object? value, string id)
{
var root = JsonSerializer.SerializeToNode(value)!.Root;

root["traceId"] = id;

return root;
}

public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
var id = Activity.Current?.Id ?? context.HttpContext.TraceIdentifier;
var serializerOptions = context.HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value.SerializerOptions;
var ret = await next(context);

return ret switch
{
IStatusCodeHttpResult c => c switch
{
IValueHttpResult v => Results.Text(AddTraceId(v.Value, id).ToJsonString(), "application/json", statusCode: c.StatusCode),
{ } v => v
},
//ValidationProblem c => AddTraceIdForFluentValidation(c),
IValueHttpResult v => Results.Text
(
AddTraceId(v.Value).ToJsonString(serializerOptions),
contentType: v switch
{
IContentTypeHttpResult c => c.ContentType,
_ => "application/json"
},
statusCode: v switch
{
IStatusCodeHttpResult c => c.StatusCode,
_ => 200
}
),
IResult v => Results.Text
(
$$"""{"traceId":"{{id}}"}""",
contentType: "application/json",
statusCode: v switch
{
IStatusCodeHttpResult c => c.StatusCode,
_ => 200
}
),
{ } v => Results.Text(AddTraceId(v).ToJsonString(serializerOptions), "application/json", statusCode: 200),
_ => ret
};
}

ValidationProblem AddTraceIdForFluentValidation(ValidationProblem v)
{
v.ProblemDetails.Extensions["traceId"] = id;
return v;
}

JsonNode AddTraceId(object? value)
{
var root = JsonSerializer.SerializeToNode(value, serializerOptions)!.Root;
root["traceId"] = id;

return root;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
using System.Diagnostics;
using System.Text.Json;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Mvc;

namespace WebApplicationMinimalApi8.ExceptionHandlers;

public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
var traceId = Activity.Current?.Id ?? httpContext.TraceIdentifier;
logger.LogError(exception, "Exception occurred: {Message}", exception.Message);

await Results.Problem(
title: exception.Message,
statusCode: StatusCodes.Status500InternalServerError,
extensions: new Dictionary<string, object?>
{
["traceId"] = traceId
}
statusCode: StatusCodes.Status500InternalServerError
).ExecuteAsync(httpContext);

return true;
Expand Down
50 changes: 30 additions & 20 deletions src/WebApplicationMinimalApi8/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,40 @@
using WebApplicationMinimalApi8.EndpointFilters;
using WebApplicationMinimalApi8.ExceptionHandlers;
using System.Linq;
using System.Diagnostics;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.ExampleFilters();
});
builder.Services.AddSwaggerGen(c => c.ExampleFilters());
builder.Services.AddSwaggerExamplesFromAssemblyOf<Program>();
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.AddFluentValidationEndpointFilter();
builder.Services.ConfigureHttpJsonOptions(options => {
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.PropertyNameCaseInsensitive = false;
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.IncludeFields = true;
options.SerializerOptions.PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate;
options.SerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { InterceptNullSetter }
};
});
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.AddFluentValidationEndpointFilter();

if (builder.Environment.IsEnvironment("Best"))
{
builder.Services.AddExceptionHandler<BadRequestExceptionHandler>();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
builder.Services.AddProblemDetails(options =>
options.CustomizeProblemDetails = (context) =>
{
if (!context.ProblemDetails.Extensions.ContainsKey("traceId"))
{
string? traceId = Activity.Current?.Id ?? context.HttpContext.TraceIdentifier;
context.ProblemDetails.Extensions.Add(new KeyValuePair<string, object?>("traceId", traceId));
}
}
);
}

var app = builder.Build();
Expand All @@ -45,19 +53,21 @@
{
app.UseExceptionHandler();
}
var root = app.MapGroup("/")
.AddEndpointFilter<ResponseAddTraceIdFilter>()
.AddFluentValidationFilter();


app.MapPost("/validate", (MessageDto message) => message)
.WithDescription("메시지를 검증합니다.")
.AddFluentValidationFilter()
.AddEndpointFilter<ResponseAddTraceIdFilter>()
.WithOpenApi();
root.MapPost("/validate", (MessageDto message) => Results.Ok())
.WithDescription("메시지를 검증합니다.")
.WithOpenApi();

app.MapGet("/500", () =>
{
throw new Exception("이런 저런 에러가 났군요");
})
.WithName("InternalException")
.WithOpenApi();
root.MapGet("/500", () =>
{
throw new Exception("이런 저런 에러가 났군요");
})
.WithName("InternalException")
.WithOpenApi();

app.Run();

Expand Down

0 comments on commit eb7201d

Please sign in to comment.