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

Introduce interfaces to describe IResult types #42187

Closed
brunolins16 opened this issue Jun 14, 2022 · 9 comments · Fixed by #42385
Closed

Introduce interfaces to describe IResult types #42187

brunolins16 opened this issue Jun 14, 2022 · 9 comments · Fixed by #42385
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc area-web-frameworks blog-candidate Consider mentioning this in the release blog post Docs This issue tracks updating documentation feature-minimal-actions Controller-like actions for endpoint routing
Milestone

Comments

@brunolins16
Copy link
Member

brunolins16 commented Jun 14, 2022

Background and Motivation

The #40656 issue made all IResult concrete types publicly available, at that time the main goal was to make the Minimal API endpoints unit tests easier. As part of the original design was decided to remove all the hierarchy and make all types sealed, however, with the introduction of the ability to add Filters to an endpoint shows that, based on community feedback, without a more generalized way to describe an IResult type (as we have in ActionResult types) the filter implementation become more complex, since is needed to have a type check for every possibility.

Proposed API

The proposal is to introduce the following interfaces that will describe the public IResult types:

namespace Microsoft.AspNetCore.Http.HttpResults;

+public interface IStatusCodeHttpResult
+{
+    int StatusCode { get; }
+}

+public interface IValueHttpResult : IStatusCodeHttpResult
+{
+    object? RawValue { get; }
+}

+public interface IValueHttpResult<TValue> : IValueHttpResult
+{
+    TValue? Value { get; }
+}

+public interface IContentHttpResult
+{
+    string? ContentType { get; }
+}

+public interface IFileHttpResult : IContentHttpResult
+{
+    string? FileDownloadName { get; }
+}

+public interface IMultiResults
+{
+    IResult Result { get; }
+}

Also, as part of the proposal the IResult types must be updated to implement the new interfaces:

namespace Microsoft.AspNetCore.Http.HttpResults;

-public sealed class Accepted : IResult, IEndpointMetadataProvider
+public sealed class Accepted : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider
+public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider,  IValueHttpResult<TValue>

-public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider,
+public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider, IValueHttpResult<TValue>

-public sealed class BadRequest : IResult, IEndpointMetadataProvider
+public sealed class BadRequest : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider
+public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider,  IValueHttpResult<TValue>

-public sealed class Conflict : IResult, IEndpointMetadataProvider
+public sealed class Conflict : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider
+public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider, IValueHttpResult<TValue>

-public sealed partial class ContentHttpResult : IResult
+public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult, IContentHttpResult

-public sealed class Created : IResult, IEndpointMetadataProvider
+public sealed class Created : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider
+public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider,  IValueHttpResult<TValue>

-public sealed class Created<TValue> : IResult, IEndpointMetadataProvider
+public sealed class Created<TValue> : IResult, IEndpointMetadataProvider, IValueHttpResult<TValue>

-public sealed partial class FileContentHttpResult : IResult
+public sealed partial class FileContentHttpResult : IResult, IFileHttpResult

-public sealed class FileStreamHttpResult : IResult
+public sealed class FileStreamHttpResult : IResult, IFileHttpResult

-public sealed partial class JsonHttpResult<TValue> : IResult
+public sealed partial class JsonHttpResult<TValue> : IResult, IValueHttpResult<TValue>, IContentHttpResult

-public class NoContent : IResult, IEndpointMetadataProvider
+public class NoContent : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class NotFound : IResult, IEndpointMetadataProvider
+public sealed class NotFound : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider
+public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider,  IValueHttpResult<TValue>

-public sealed class Ok : IResult, IEndpointMetadataProvider
+public sealed class Ok : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider
+public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider,  IValueHttpResult<TValue>

-public sealed partial class PhysicalFileHttpResult : IResult
+public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult

-public sealed class PushStreamHttpResult : IResult
+public sealed class PushStreamHttpResult : IResult, IFileHttpResult

-public sealed partial class StatusCodeHttpResult : IResult
+public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult

-public sealed class UnauthorizedHttpResult : IResult
+public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult

-public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider
+public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult

-public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider
+public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider,  IValueHttpResult<TValue>

-public sealed class VirtualFileHttpResult : IResult
+public sealed class VirtualFileHttpResult : IResult, IFileHttpResult

-public sealed class Results<TResult1, TResultN> : IResult, IEndpointMetadataProvider
+public sealed class Results<TResult1, TResultN> : IResult, IMultiResults, IEndpointMetadataProvider

Usage Examples

app.MapGet("/weatherforecast", () =>
{
    WeatherForecast[] forecast = WeatherForecast.GetForecast();
    return Results.Ok(forecast);
})
.AddFilter(async (context, next) =>
{
    var result = await next(context);

    return result switch
    {
        IValueHttpResult<WeatherForecast[]> weatherResut => new WeatherForecastsResult(weatherResut.Value, weatherResut.StatusCode),        
        IValueHttpResult valueResult => Results.Extensions.Xml(valueResult.RawValue, valueResult.StatusCode),
        _ => result
    };
});

Alternative Designs

A simple hierarchy is proposed in my original design, that helps users to create their code, however, an alternative design is remove the hierarchy (as same as the public available types) and update the IResult types to implement all of them explicitly. Eg.:

namespace Microsoft.AspNetCore.Http.HttpResults;

+public interface IStatusCodeHttpResult
+{
+    int StatusCode { get; }
+}

+public interface IValueHttpResult
+{
+    object? RawValue { get; }
+}

+public interface IValueHttpResult<TValue>
+{
+    TValue? Value { get; }
+}

+public interface IContentHttpResult
+{
+    string? ContentType { get; }
+}

+public interface IFileHttpResult : IContentHttpResult
+{
+    string? FileDownloadName { get; }
+}

+public interface IMultiResults
+{
+    IResult Result { get; }
+}


-public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider
+public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>

Risks

I don't see a risk in the API proposal since it only introduces a new set of interfaces.

@brunolins16 brunolins16 added api-suggestion Early API idea and discussion, it is NOT ready for implementation feature-minimal-actions Controller-like actions for endpoint routing area-web-frameworks labels Jun 14, 2022
@brunolins16 brunolins16 self-assigned this Jun 14, 2022
@brunolins16 brunolins16 added this to the 7.0-preview7 milestone Jun 14, 2022
@brunolins16 brunolins16 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 Jun 15, 2022
@ghost
Copy link

ghost commented Jun 15, 2022

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.

@BrennanConroy
Copy link
Member

API review notes:

  • Does public interface IValueHttpResult<TValue> : IValueHttpResult need to be marked with out to be covariant?
    • Probably, let's test it
    • How does a struct T work?
  • Do we even need public interface IValueHttpResult now that all return types are generic?
    • No, let's remove it, checking for "does my result have an object?" can be done with checking for IValueHttpResult<object>
  • Should the interface types inherit from other interfaces? (e.g. public interface IFileHttpResult : IContentHttpResult)
    • No, let's keep them separate
  • IMultiResult is an odd name, it implies there are multiple results
    • INestedResult might be better, let's think about it

API needs update, @brunolins16 will update with the full diff once testing a couple things.

@BrennanConroy BrennanConroy added api-needs-work API needs work before it is approved, it is NOT ready for implementation and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Jun 21, 2022
@brunolins16
Copy link
Member Author

  • How does a struct T work?

The covariance/contravariance works for reference types only (SharpLab sample):

image

With that, I think this is not true if using struct:

  • Do we even need public interface IValueHttpResult now that all return types are generic?
    • No, let's remove it, checking for "does my result have an object?" can be done with checking for IValueHttpResult<object>

I feel that it might be confusing and probably keeping the public interface IValueHttpResult might be simpler for users.

@brunolins16
Copy link
Member Author

Here, is the updated proposal, keeping the IValueHttpResult:

### Interfaces

namespace Microsoft.AspNetCore.Http
{
+ public interface IStatusCodeHttpResult
+ {
+     int StatusCode { get; }
+ }
 
+ public interface IValueHttpResult
+ {
+     object? RawValue { get; }
+ }
 
+ public interface IValueHttpResult<out TValue>
+ {
+     TValue? Value { get; }
+ }
 
+ public interface IContentTypeHttpResult
+ {
+     string? ContentType { get; }
+ }
 
+ public interface IFileHttpResult
+ {
+     string? ContentType { get; }
+     string? FileDownloadName { get; }
+ }
 
+ public interface INestedHttpResult
+ {
+     IResult Result { get; }
+ }
}

### Result Types

namespace Microsoft.AspNetCore.Http.HttpResults;
{

- public sealed class Accepted : IResult, IEndpointMetadataProvider
+ public sealed class Accepted : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider
+ public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class BadRequest : IResult, IEndpointMetadataProvider
+ public sealed class BadRequest : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Conflict : IResult, IEndpointMetadataProvider
+ public sealed class Conflict : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class ContentHttpResult : IResult
+ public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult, IContentTypeHttpResult
 
- public sealed class Created : IResult, IEndpointMetadataProvider
+ public sealed class Created : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider
+ public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Created<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Created<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class FileContentHttpResult : IResult
+ public sealed partial class FileContentHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class FileStreamHttpResult : IResult
+ public sealed class FileStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed partial class JsonHttpResult<TValue> : IResult
+ public sealed partial class JsonHttpResult<TValue> : IResult, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>, IContentTypeHttpResult
 
- public class NoContent : IResult, IEndpointMetadataProvider
+ public class NoContent : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class NotFound : IResult, IEndpointMetadataProvider
+ public sealed class NotFound : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Ok : IResult, IEndpointMetadataProvider
+ public sealed class Ok : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class PhysicalFileHttpResult : IResult
+ public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class ProblemHttpResult : IResult
+ public sealed class ProblemHttpResult : IResult, IStatusCodeHttpResult, IContentTypeHttpResult, IValueHttpResult, IValueHttpResult<ProblemDetails>
{ 
-      public int? StatusCode { get; }
+      public int StatusCode { get; }
} 
 
- public sealed class PushStreamHttpResult : IResult
+ public sealed class PushStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class Results<TResult1, TResultN> : IResult, IEndpointMetadataProvider
+ public sealed class Results<TResult1, TResultN> : IResult, INestedHttpResult, IEndpointMetadataProvider
 
- public sealed partial class StatusCodeHttpResult : IResult
+ public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult
 
- public sealed class UnauthorizedHttpResult : IResult
+ public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult
 
- public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider
+ public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class ValidationProblem : IResult, IEndpointMetadataProvider
+ public sealed class ValidationProblem : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IContentTypeHttpResult, IValueHttpResult, IValueHttpResult<HttpValidationProblemDetails>
 
- public sealed class VirtualFileHttpResult : IResult
+ public sealed class VirtualFileHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
}

@brunolins16 brunolins16 added api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews and removed api-needs-work API needs work before it is approved, it is NOT ready for implementation labels Jun 27, 2022
@ghost
Copy link

ghost commented Jun 27, 2022

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.

@BrennanConroy
Copy link
Member

BrennanConroy commented Jun 27, 2022

API review notes:

  • Should we keep IValueHttpResult?
    • Struct values don't work with covariance result is IValueHttpResult<object?> for example, so this allows generic code to look for any result value, struct or object
  • Status code changing to non-nullable?
    • I think it makes sense, we don't want to apply the interface to a type if it might not have a status code
  • Should the interfaces be in Microsoft.AspNetCore.Http.HttpResults or Microsoft.AspNetCore.Http?
    • Microsoft.AspNetCore.Http is good, code with TypeResults will have the Microsoft.AspNetCore.Http namespace already and so these will show up without introducing a new namespace.
namespace Microsoft.AspNetCore.Http
{
+ public interface IStatusCodeHttpResult
+ {
+     int StatusCode { get; }
+ }
 
+ public interface IValueHttpResult
+ {
+     object? RawValue { get; }
+ }
 
+ public interface IValueHttpResult<out TValue>
+ {
+     TValue? Value { get; }
+ }
 
+ public interface IContentTypeHttpResult
+ {
+     string? ContentType { get; }
+ }
 
+ public interface IFileHttpResult
+ {
+     string? ContentType { get; }
+     string? FileDownloadName { get; }
+ }
 
+ public interface INestedHttpResult
+ {
+     IResult Result { get; }
+ }
}

### Result Types

namespace Microsoft.AspNetCore.Http.HttpResults;
{

- public sealed class Accepted : IResult, IEndpointMetadataProvider
+ public sealed class Accepted : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider
+ public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class BadRequest : IResult, IEndpointMetadataProvider
+ public sealed class BadRequest : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Conflict : IResult, IEndpointMetadataProvider
+ public sealed class Conflict : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class ContentHttpResult : IResult
+ public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult, IContentTypeHttpResult
 
- public sealed class Created : IResult, IEndpointMetadataProvider
+ public sealed class Created : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider
+ public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Created<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Created<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class FileContentHttpResult : IResult
+ public sealed partial class FileContentHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class FileStreamHttpResult : IResult
+ public sealed class FileStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed partial class JsonHttpResult<TValue> : IResult
+ public sealed partial class JsonHttpResult<TValue> : IResult, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>, IContentTypeHttpResult
 
- public class NoContent : IResult, IEndpointMetadataProvider
+ public class NoContent : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class NotFound : IResult, IEndpointMetadataProvider
+ public sealed class NotFound : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Ok : IResult, IEndpointMetadataProvider
+ public sealed class Ok : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class PhysicalFileHttpResult : IResult
+ public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class ProblemHttpResult : IResult
+ public sealed class ProblemHttpResult : IResult, IStatusCodeHttpResult, IContentTypeHttpResult, IValueHttpResult, IValueHttpResult<ProblemDetails>
{ 
-      public int? StatusCode { get; }
+      public int StatusCode { get; }
} 
 
- public sealed class PushStreamHttpResult : IResult
+ public sealed class PushStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class Results<TResult1, TResultN> : IResult, IEndpointMetadataProvider
+ public sealed class Results<TResult1, TResultN> : IResult, INestedHttpResult, IEndpointMetadataProvider
 
- public sealed partial class StatusCodeHttpResult : IResult
+ public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult
 
- public sealed class UnauthorizedHttpResult : IResult
+ public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult
 
- public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider
+ public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class ValidationProblem : IResult, IEndpointMetadataProvider
+ public sealed class ValidationProblem : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IContentTypeHttpResult, IValueHttpResult, IValueHttpResult<HttpValidationProblemDetails>
 
- public sealed class VirtualFileHttpResult : IResult
+ public sealed class VirtualFileHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
}

API Approved!

@BrennanConroy BrennanConroy 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 Jun 27, 2022
@brunolins16
Copy link
Member Author

@BrennanConroy when I change the hierarchy, I forgot to change the IValueHttpResult.RawValue to IValueHttpResult.Value that make more sense and was not possible before. Can I get another approval for this small change?

 public interface IValueHttpResult
 {
-    object? RawValue { get; }
+    object? Value { get; }
 }

@brunolins16 brunolins16 added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Jul 5, 2022
@ghost
Copy link

ghost commented Jul 5, 2022

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.

@brunolins16 brunolins16 added the Docs This issue tracks updating documentation label Jul 5, 2022
@halter73
Copy link
Member

halter73 commented Jul 6, 2022

API review notes:

  • Makes sense for consistency.
  • API approved!
namespace Microsoft.AspNetCore.Http
{
+ public interface IStatusCodeHttpResult
+ {
+     int StatusCode { get; }
+ }
 
+ public interface IValueHttpResult
+ {
+     object? Value { get; }
+ }
 
+ public interface IValueHttpResult<out TValue>
+ {
+     TValue? Value { get; }
+ }
 
+ public interface IContentTypeHttpResult
+ {
+     string? ContentType { get; }
+ }
 
+ public interface IFileHttpResult
+ {
+     string? ContentType { get; }
+     string? FileDownloadName { get; }
+ }
 
+ public interface INestedHttpResult
+ {
+     IResult Result { get; }
+ }
}

### Result Types

namespace Microsoft.AspNetCore.Http.HttpResults;
{

- public sealed class Accepted : IResult, IEndpointMetadataProvider
+ public sealed class Accepted : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider
+ public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Accepted<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class BadRequest : IResult, IEndpointMetadataProvider
+ public sealed class BadRequest : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class BadRequest<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Conflict : IResult, IEndpointMetadataProvider
+ public sealed class Conflict : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Conflict<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class ContentHttpResult : IResult
+ public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult, IContentTypeHttpResult
 
- public sealed class Created : IResult, IEndpointMetadataProvider
+ public sealed class Created : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider
+ public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Created<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Created<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class FileContentHttpResult : IResult
+ public sealed partial class FileContentHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class FileStreamHttpResult : IResult
+ public sealed class FileStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed partial class JsonHttpResult<TValue> : IResult
+ public sealed partial class JsonHttpResult<TValue> : IResult, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>, IContentTypeHttpResult
 
- public class NoContent : IResult, IEndpointMetadataProvider
+ public class NoContent : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class NotFound : IResult, IEndpointMetadataProvider
+ public sealed class NotFound : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class NotFound<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class Ok : IResult, IEndpointMetadataProvider
+ public sealed class Ok : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class Ok<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed partial class PhysicalFileHttpResult : IResult
+ public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class ProblemHttpResult : IResult
+ public sealed class ProblemHttpResult : IResult, IStatusCodeHttpResult, IContentTypeHttpResult, IValueHttpResult, IValueHttpResult<ProblemDetails>
{ 
-      public int? StatusCode { get; }
+      public int StatusCode { get; }
} 
 
- public sealed class PushStreamHttpResult : IResult
+ public sealed class PushStreamHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
 
- public sealed class Results<TResult1, TResultN> : IResult, IEndpointMetadataProvider
+ public sealed class Results<TResult1, TResultN> : IResult, INestedHttpResult, IEndpointMetadataProvider
 
- public sealed partial class StatusCodeHttpResult : IResult
+ public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult
 
- public sealed class UnauthorizedHttpResult : IResult
+ public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult
 
- public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider
+ public sealed class UnprocessableEntity : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult
 
- public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider
+ public sealed class UnprocessableEntity<TValue> : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IValueHttpResult, IValueHttpResult<TValue>
 
- public sealed class ValidationProblem : IResult, IEndpointMetadataProvider
+ public sealed class ValidationProblem : IResult, IEndpointMetadataProvider, IStatusCodeHttpResult, IContentTypeHttpResult, IValueHttpResult, IValueHttpResult<HttpValidationProblemDetails>
 
- public sealed class VirtualFileHttpResult : IResult
+ public sealed class VirtualFileHttpResult : IResult, IFileHttpResult, IContentTypeHttpResult
}

@halter73 halter73 removed the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Jul 6, 2022
@brunolins16 brunolins16 added the blog-candidate Consider mentioning this in the release blog post label Aug 4, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 3, 2022
@amcasey amcasey added the area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc label Jun 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc area-web-frameworks blog-candidate Consider mentioning this in the release blog post Docs This issue tracks updating documentation feature-minimal-actions Controller-like actions for endpoint routing
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants