Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProblemDetails is not showing the trace id when AddProblemDetails is set before AddControllers #57935

Open
1 task done
MigueZS opened this issue Sep 18, 2024 · 2 comments
Open
1 task done
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-problem-details help wanted Up for grabs. We would accept a PR to help resolve this issue

Comments

@MigueZS
Copy link

MigueZS commented Sep 18, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

A .NET 8.0 application with ProblemDetails enabled not showing the traceId when AddProblemDetails is called before AddControllers. This happens in both, when using the WebApplicationBuilder or using the CreateDefaultWebhost with an startup class. This only happens when the Accept header sent by the client is application/json.

Expected Behavior

When an exception occurs in one controller, the problem details response shows the traceId or the documentation clearly warns you to call AddProblemDetails after the AddControllers call.

Order is important in middleware but not when configuring services.

Steps To Reproduce

Create a new .net 8.0 application with the following Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseExceptionHandler();
app.UseStatusCodePages();

app.UseHttpsRedirection();

app.MapControllers();

app.Run();

Create a controller that throws an exception

[ApiController]
[Route("[controller]")]
public class PDController : ControllerBase
{
    [HttpGet]
    [Route("exception")]
    public IActionResult GetSystemException()
    {
        throw new Exception("This is a test exception");
    }
}

Call the endpoint, the traceId is shown regardless of the Accept header sent

image

Swap the order of the AddControllers and AddProblemDetails calls in the Program.cs

builder.Services.AddProblemDetails();
builder.Services.AddControllers();

Call the endpoint and the traceId is not shown when the Accept header is "application/json"

image

Any other Accept header will produce the traceId

image

Exceptions (if any)

No response

.NET Version

8.0.400

Anything else?

Tested in Visual Studio 2022 or running dotnet run from the console.

Dotnet info output

.NET SDK:
 Version:           8.0.400
 Commit:            36fe6dda56
 Workload version:  8.0.400-manifests.6c274a57
 MSBuild version:   17.11.3+0c8610977

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22000
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\8.0.400\

.NET workloads installed:
Configured to use loose manifests when installing new manifests.
 [aspire]
   Installation Source: VS 17.11.35303.130
   Manifest Version:    8.1.0/8.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.1.0\WorkloadManifest.json
   Install Type:        FileBased


Host:
  Version:      8.0.8
  Architecture: x64
  Commit:       08338fcaa5
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Sep 18, 2024
@captainsafia
Copy link
Member

@MigueZS Thanks for reporting this issue!

I believe the problem is a result of the logic that the ProblemDetailsService employs to determine which ProblemDetailsWriter it should use to emit the ProblemDetails object:

// Try to write using all registered writers
// sequentially and stop at the first one that
// `canWrite`.
for (var i = 0; i < _writers.Length; i++)
{
var selectedWriter = _writers[i];
if (selectedWriter.CanWrite(context))
{
await selectedWriter.WriteAsync(context);
return true;
}
}

When AddControllers is called, it registers the default implementation used by MVC, which is defined here.

services.TryAddEnumerable(ServiceDescriptor.Singleton<IProblemDetailsWriter, DefaultApiProblemDetailsWriter>());

When AddProblemDetails is called, it registers its own default implementation.

services.TryAddEnumerable(ServiceDescriptor.Singleton<IProblemDetailsWriter, DefaultProblemDetailsWriter>());

The Write logic in each of these is slightly different which is what I suspect is causing this issue. I suspect in this particular case it's MVC's ProblemDetailsWriter that is being called since it doesn't support setting a trace ID. To resolve this, we'll need to update the DefaultProblemDetailsFactory to emit a trace ID as extension data.

Would you be interested in submitting a PR with this fix?

@captainsafia captainsafia added help wanted Up for grabs. We would accept a PR to help resolve this issue labels Sep 19, 2024
@MigueZS
Copy link
Author

MigueZS commented Sep 19, 2024

Thank you for your response.

I found that your suggestion has already been applied: 9f4eb97. Please correct me if I am wrong.

If so can you please answer the following questions:

  • The fix was tagged for .net 9.0. Should I suppose the fix will only be available there and never ported back to 8.0?
  • It is still a little weird that this 2 register 2 different implementations. In that case what it would be the preferred recommended one to use if inverting the calls derives in those 2 different writers?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-problem-details help wanted Up for grabs. We would accept a PR to help resolve this issue
Projects
None yet
Development

No branches or pull requests

2 participants