Skip to content

Commit

Permalink
Fixes Problem Details casing bug (#59396) (#59876)
Browse files Browse the repository at this point in the history
* Fixes Problem Details casing bug (#59396)

* Apply suggestions from code review

---------

Co-authored-by: Safia Abdalla <safia@safia.rocks>
  • Loading branch information
scottlwalker and captainsafia authored Jan 15, 2025
1 parent d4880ed commit 8aec8fd
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public ValueTask WriteAsync(ProblemDetailsContext context)
ProblemDetailsDefaults.Apply(context.ProblemDetails, httpContext.Response.StatusCode);

var traceId = Activity.Current?.Id ?? httpContext.TraceIdentifier;
context.ProblemDetails.Extensions["traceId"] = traceId;
var traceIdKeyName = _serializerOptions.PropertyNamingPolicy?.ConvertName("traceId") ?? "traceId";
context.ProblemDetails.Extensions[traceIdKeyName] = traceId;

_options.CustomizeProblemDetails?.Invoke(context);

Expand Down
55 changes: 53 additions & 2 deletions src/Http/Http.Extensions/test/ProblemDetailsDefaultWriterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public async Task WriteAsync_Works_ProperCasing()
//Assert
stream.Position = 0;
var result = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(stream, JsonSerializerOptions.Default);
Assert.Equal(result.Keys, new(new() { { "type", 0 }, { "title", 1 }, { "status", 2 }, { "detail", 3 }, { "instance", 4 }, { "extensionKey", 5 }, {"traceId", expectedTraceId } }));
Assert.Equal(result.Keys, new(new() { { "type", 0 }, { "title", 1 }, { "status", 2 }, { "detail", 3 }, { "instance", 4 }, { "extensionKey", 5 }, { "traceId", expectedTraceId } }));
}

[Fact]
Expand Down Expand Up @@ -117,7 +117,7 @@ public async Task WriteAsync_Works_ProperCasing_ValidationProblemDetails()
//Assert
stream.Position = 0;
var result = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(stream, JsonSerializerOptions.Default);
Assert.Equal(result.Keys, new(new() { { "type", 0 }, { "title", 1 }, { "status", 2 }, { "detail", 3 }, { "instance", 4 }, { "errors", 5 }, {"traceId", expectedTraceId } }));
Assert.Equal(result.Keys, new(new() { { "type", 0 }, { "title", 1 }, { "status", 2 }, { "detail", 3 }, { "instance", 4 }, { "errors", 5 }, { "traceId", expectedTraceId } }));
}

[Fact]
Expand Down Expand Up @@ -689,6 +689,57 @@ private static HttpContext CreateContext(
return context;
}

[Theory]
[InlineData("SnakeCaseLower", "trace_id")]
[InlineData("CamelCase", "traceId")]
[InlineData("KebabCaseLower", "trace-id")]
[InlineData("KebabCaseUpper", "TRACE-ID")]
[InlineData("SnakeCaseUpper", "TRACE_ID")]
public async Task TestPropertyNamingPolicyChanges(string caseSelection, string extensionVariableName)
{
// Arrange
JsonNamingPolicy propertyNamingPolicy = caseSelection switch
{
"CamelCase" => JsonNamingPolicy.CamelCase,
"KebabCaseLower" => JsonNamingPolicy.KebabCaseLower,
"KebabCaseUpper" => JsonNamingPolicy.KebabCaseUpper,
"SnakeCaseLower" => JsonNamingPolicy.SnakeCaseLower,
"SnakeCaseUpper" => JsonNamingPolicy.SnakeCaseUpper,
_ => JsonNamingPolicy.KebabCaseLower
};

var options = new JsonOptions();
options.SerializerOptions.PropertyNamingPolicy = propertyNamingPolicy;

var writer = GetWriter(jsonOptions: options);
var stream = new MemoryStream();
var context = CreateContext(stream);

var expectedTraceId = Activity.Current?.Id ?? context.TraceIdentifier;
var expectedProblem = new ProblemDetails()
{
Detail = "Custom Bad Request",
Instance = "Custom Bad Request",
Status = StatusCodes.Status400BadRequest,
Type = "https://tools.ietf.org/html/rfc9110#section-15.5.1-custom",
Title = "Custom Bad Request",
};
var problemDetailsContext = new ProblemDetailsContext()
{
HttpContext = context,
ProblemDetails = expectedProblem
};

//Act
await writer.WriteAsync(problemDetailsContext);
stream.Position = 0;
using var reader = new StreamReader(stream);
var json = await reader.ReadToEndAsync();

//Assert
Assert.Contains($"\"{extensionVariableName}\":\"{expectedTraceId}\"", json);
}

private static IServiceProvider CreateServices()
{
var services = new ServiceCollection();
Expand Down

0 comments on commit 8aec8fd

Please sign in to comment.