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

Add a AddDefaultProblemDetailsFactory method that allows registering the internal DefaultProblemDetailsFactory class with the container #53030

Closed
Carl-Hugo opened this issue Dec 27, 2023 · 5 comments · Fixed by #55666
Labels
api-approved API was approved in API review, it can be implemented area-web-frameworks help wanted Up for grabs. We would accept a PR to help resolve this issue
Milestone

Comments

@Carl-Hugo
Copy link

Background and Motivation

Quote from #49982:

I aim to leverage a ProblemDetailsFactory implementation (say the DefaultProblemDetailsFactory class) without adding MVC services to a Minimal API project.

More context: I'm updating older code to support .NET 7-8 that uses the ProblemDetailsFactory to generate ProblemDetails instances. The workaround was to call AddMvcCore then, which was kinda ok since there was only MVC before. However, now that we have Minimal APIs, adding MVC dependencies to a non-MVC project makes no sense.

On top of that, the proposed API will bring the two Problem Details spaces one tiny step closer and more unified without introducing any breaking changes.

Proposed API

The proposed API is to add a new AddDefaultProblemDetailsFactory extension method that registers the default DefaultProblemDetailsFactory class and update the AddMvcCore method to leverage that extension instead.

Since the Microsoft.AspNetCore.Http abstractions seem like a better place than MVC to consolidate Problem Details classes, I suggest adding the extension method there if possible. The suggested AddDefaultProblemDetailsFactory method would look like this:

namespace Microsoft.Extensions.DependencyInjection;
public static class DefaultProblemDetailsFactoryExtensions
{
    public static IServiceCollection AddDefaultProblemDetailsFactory(this IServiceCollection services)
    {
        services.TryAddSingleton<ProblemDetailsFactory, DefaultProblemDetailsFactory>();
        // Optional:
         services.TryAddEnumerable(ServiceDescriptor.Singleton<IProblemDetailsWriter, DefaultApiProblemDetailsWriter>());
    }
}

Update to MVC AddMvcCoreServices method:

// ...
namespace Microsoft.Extensions.DependencyInjection;
public static class MvcCoreServiceCollectionExtensions
{
    // ...
    internal static void AddMvcCoreServices(IServiceCollection services)
    {
        // ...
        // ProblemDetails
+       services.AddDefaultProblemDetailsFactory();
-       services.TryAddSingleton<ProblemDetailsFactory, DefaultProblemDetailsFactory>();
        // Optional: 
-       services.TryAddEnumerable(ServiceDescriptor.Singleton<IProblemDetailsWriter, DefaultApiProblemDetailsWriter>());
    }
}

See the MvcCoreServiceCollectionExtensions class.

In case the team wants to consolidate the optional dependency from the preceding code blocks, they could also consider adapting the AddProblemDetails method of the ProblemDetailsServiceCollectionExtensions class to use this extension as well:

// ...
namespace Microsoft.Extensions.DependencyInjection;
public static class ProblemDetailsServiceCollectionExtensions
{
    // ...
    public static IServiceCollection AddProblemDetails(
        this IServiceCollection services,
        Action<ProblemDetailsOptions>? configure)
    {
        // ...
+       services.AddDefaultProblemDetailsFactory();
-        services.TryAddEnumerable(ServiceDescriptor.Singleton<IProblemDetailsWriter, DefaultProblemDetailsWriter>());
        // ...
    }
}

Of course, doing what the preceding code block suggests would add an additional dependency not used by the ProblemDetailsService class (the ProblemDetailsFactory) but would centralize the registering of the default IProblemDetailsWriter, which would guarantee that both MVC and Minimal APIs are aligned in their default implementation.

Usage Examples

One could register the factory like this (Program.cs):

builder.Services.AddDefaultProblemDetailsFactory();

Alternative Designs

Please refer to the following issue for more info about alternate designs and thoughts put into this proposal in conjunction with @captainsafia: #49982.

Risks

No foreseeable risks.

@Carl-Hugo Carl-Hugo added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Dec 27, 2023
@captainsafia captainsafia added api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Dec 28, 2023
@ghost
Copy link

ghost commented Dec 28, 2023

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@halter73 halter73 added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Jan 4, 2024
@halter73
Copy link
Member

halter73 commented Jan 4, 2024

API Review Notes:

  • How do we explain when to call AddProblemDetails vs AddProblemDetailsFactory?
  • It seems that without MVC, this is only usable if you resolve the ProblemDetailsFactory directly in your code.
  • We think this is very niche, and would probably only be used by app migrating away from MVC, but making an internal service accessible isn't too costly.
  • Let's just make the type and constructor public.

API Approved!

namespace Microsoft.AspNetCore.Mvc.Infrastructure;

- internal sealed class DefaultProblemDetailsFactory : ProblemDetailsFactory
+ public sealed class DefaultProblemDetailsFactory : ProblemDetailsFactory
{
+    public DefaultProblemDetailsFactory(IOptions<ApiBehaviorOptions> options,  IOptions<ProblemDetailsOptions>? problemDetailsOptions = null);
+    public override ProblemDetails CreateProblemDetails(HttpContext httpContext, int? statusCode = null, string? title = null,   string? type = null, string? detail = null, string? instance = null);
+    public override ValidationProblemDetails CreateValidationProblemDetails(HttpContext httpContext, ModelStateDictionary modelStateDictionary, int? statusCode = null, string? title = null, string? type = null, string? detail = null, string? instance = null);
}

@Carl-Hugo
Copy link
Author

[...]
Let's just make the type and constructor public.
[...]
API Approved!
[...]

Awesome; making the type public was my first suggestion in the discussion thread, so this works for me.

Should I plan to wait until .NET 9 to see this change, or can this be released before?

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@captainsafia
Copy link
Member

Sticking this in the backlog for now since it is low-pri.

However, I'd be happy to review a PR for this given that the API has already been approved. Marking as help wanted as well.

@captainsafia captainsafia added the help wanted Up for grabs. We would accept a PR to help resolve this issue label May 1, 2024
@captainsafia captainsafia added this to the Backlog milestone May 1, 2024
Copy link
Contributor

Looks like this issue has been identified as a candidate for community contribution. If you're considering sending a PR for this issue, look for the Summary Comment link in the issue description. That comment has been left by an engineer on our team to help you get started with handling this issue. You can learn more about our Help Wanted process here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-web-frameworks help wanted Up for grabs. We would accept a PR to help resolve this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants