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

Make Result types constructors public #40802

Closed
brunolins16 opened this issue Mar 21, 2022 · 6 comments
Closed

Make Result types constructors public #40802

brunolins16 opened this issue Mar 21, 2022 · 6 comments
Assignees
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels feature-minimal-actions Controller-like actions for endpoint routing
Milestone

Comments

@brunolins16
Copy link
Member

brunolins16 commented Mar 21, 2022

Background and Motivation

Result types were changed to be public but was decided to keep ctors internal to have a better design discussion.

The original proposal is described in #40656

Proposed API

namespace Microsoft.AspNetCore.Http;

public class EmptyHttpResult : IResult
{
-    public static readonly EmptyHttpResult Instance { get; } = new();
+    public EmptyHttpResult() {} 
}

public sealed partial class StatusCodeHttpResult : IResult
{
+    public StatusCodeHttpResult(int statusCode) {}
}

public class NoContentHttpResult : IResult
{
+    public NoContentHttpResult() {}
}

public class UnauthorizedHttpResult : IResult
{
+    public UnauthorizedHttpResult() {} 
}

public sealed class AcceptedAtRouteHttpResult : IResult
{
+    public AcceptedAtRouteHttpResult(string? routeName = null, 
+                                     object? routeValues = null, 
+                                     object? value = null) {}
}

public sealed class CreatedAtRouteHttpResult : IResult
{
+    public CreatedAtRouteHttpResult(string? routeName = null, 
+                                     object? routeValues = null, 
+                                     object? value = null) {}
}

public sealed class AcceptedHttpResult : IResult
{
+    public AcceptedHttpResult(string? location, object? value = null) {}
+    public AcceptedHttpResult(Uri locationUri, object? value = null) {}
}

public sealed class CreatedHttpResult : IResult
{
+    public CreatedHttpResult(string? location = null, object? value = null) {}
+    public CreatedHttpResult(Uri locationUri = null, object? value = null) {}
}

public sealed class BadRequestObjectHttpResult : IResult
{
+    public BadRequestObjectHttpResult(object? error = null) {}
}

public sealed class ConflictObjectHttpResult : IResult
{
+    public ConflictObjectHttpResult(object? error = null) {}
}

public sealed class NotFoundObjectHttpResult : IResult
{
+    public NotFoundObjectHttpResult(object? value = null) {}
}

public sealed class OkObjectHttpResult : IResult
{
+    public OkObjectHttpResult(object? value = null) {}
}

public sealed class UnprocessableEntityObjectHttpResult : IResult
{
+    public UnprocessableEntityObjectHttpResult(object? value = null) {}
}

public sealed class ProblemHttpResult : IResult
{
+    public ProblemHttpResult(ProblemDetails problemDetails) {}
+    public ProblemHttpResult(
+        string? detail = null,
+        string? instance = null,
+        int? statusCode = null,
+        string? title = null,
+        string? type = null,
+        IDictionary<string, object?>? extensions = null) {}
}

public sealed class JsonHttpResult : IResult
{
+    public JsonHttpResult(object? value = null, JsonSerializerOptions? jsonSerializerOptions = null) {}

-    public string ContentType { get; }
+    public string ContentType { get; init; }

-    public int? StatusCode { get; }
+    public int? StatusCode { get; init; }
}

public sealed partial class ContentHttpResult : IResult
{
+    public ContentHttpResult(string? content = null, string? contentType = null) {}

-    public int? StatusCode { get; }
+    public int? StatusCode { get; init; }
}

public sealed partial class FileContentHttpResult : IResult
{
+    public FileContentHttpResult(ReadOnlyMemory<byte> fileContents, string? contentType = null) {}

-    public bool EnableRangeProcessing { get; }
+    public bool EnableRangeProcessing { get; init; }

-    public EntityTagHeaderValue? EntityTag { get; }
+    public EntityTagHeaderValue? EntityTag { get; init; }

-    public string? FileDownloadName { get; }
+    public string? FileDownloadName { get; init; }

-    public DateTimeOffset? LastModified { get; }
+    public DateTimeOffset? LastModified { get; init; }
}

public sealed class FileStreamHttpResult : IResult
{
+    public FileStreamHttpResult(Stream fileStream, string? contentType = null) {}

-    public bool EnableRangeProcessing { get; }
+    public bool EnableRangeProcessing { get; init; }

-    public EntityTagHeaderValue? EntityTag { get; }
+    public EntityTagHeaderValue? EntityTag { get; init; }

-    public string? FileDownloadName { get; }
+    public string? FileDownloadName { get; init; }

-    public DateTimeOffset? LastModified { get; }
+    public DateTimeOffset? LastModified { get; init; }
}

public sealed partial class PhysicalFileHttpResult : IResult
{
+    public PhysicalFileHttpResult(string fileName, string? contentType = null) {}

-    public bool EnableRangeProcessing { get; }
+    public bool EnableRangeProcessing { get; init; }

-    public EntityTagHeaderValue? EntityTag { get; }
+    public EntityTagHeaderValue? EntityTag { get; init; }

-    public string? FileDownloadName { get; }
+    public string? FileDownloadName { get; init; }

-    public DateTimeOffset? LastModified { get; }
+    public DateTimeOffset? LastModified { get; init; }
}

public sealed class PushStreamHttpResult : IResult
{
+    public PushStreamHttpResult(Func<Stream, Task> streamWriterCallback, string? contentType = null) {}

-    public bool EnableRangeProcessing { get; }
+    public bool EnableRangeProcessing { get; init; }

-    public EntityTagHeaderValue? EntityTag { get; }
+    public EntityTagHeaderValue? EntityTag { get; init; }

-    public string? FileDownloadName { get; }
+    public string? FileDownloadName { get; init; }

-    public DateTimeOffset? LastModified { get; }
+    public DateTimeOffset? LastModified { get; init; }
}

public sealed partial class VirtualFileHttpResult : IResult
{
+    public VirtualFileHttpResult(string fileName, string? contentType = null) {}

-    public bool EnableRangeProcessing { get; }
+    public bool EnableRangeProcessing { get; init; }

-    public EntityTagHeaderValue? EntityTag { get; }
+    public EntityTagHeaderValue? EntityTag { get; init; }

-    public string? FileDownloadName { get; }
+    public string? FileDownloadName { get; init; }

-    public DateTimeOffset? LastModified { get; }
+    public DateTimeOffset? LastModified { get; init; }
}

public sealed partial class ChallengeHttpResult : IResult
{
+    public ChallengeHttpResult(AuthenticationProperties? properties = null) {}
+    public ChallengeHttpResult(AuthenticationProperties? properties, string authenticationScheme) {}
+    public ChallengeHttpResult(AuthenticationProperties? properties, IList<string> authenticationSchemes) {}
}

public sealed partial class ForbidHttpResult : IResult
{
+    public ForbidHttpResult (AuthenticationProperties? properties = null) {}
+    public ForbidHttpResult (AuthenticationProperties? properties, string authenticationScheme) {}
+    public ForbidHttpResult (AuthenticationProperties? properties, IList<string> authenticationSchemes) {}
}

public sealed partial class SignOutHttpResult : IResult
{
+    public SignOutHttpResult (AuthenticationProperties? properties = null) {}
+    public SignOutHttpResult (AuthenticationProperties? properties, string authenticationScheme) {}
+    public SignOutHttpResult (AuthenticationProperties? properties, IList<string> authenticationSchemes) {}
}

public sealed partial class SignInHttpResult : IResult
{
+    public SignInHttpResult(ClaimsPrincipal principal) {}

-    public string? AuthenticationScheme { get; }
+    public string? AuthenticationScheme { get; init; }

-    public AuthenticationProperties? Properties { get; }
+    public AuthenticationProperties? Properties { get; init; }
}

public sealed partial class RedirectHttpResult : IResult
{
+    public RedirectHttpResult(string url, bool acceptLocalUrlOnly = false) {}

-    public bool Permanent { get; }
+    public bool Permanent { get; init; }

-    public bool PreserveMethod { get; }
+    public bool PreserveMethod { get; init; }
}

public sealed partial class RedirectToRouteHttpResult : IResult
{
+    public RedirectToRouteHttpResult(string? routeName = null, object? routeValues = null) {}

-    public bool Permanent { get; }
+    public bool Permanent { get; init; }

-    public bool PreserveMethod { get; }
+    public bool PreserveMethod { get; init; }

-    public string Fragment { get; }
+    public string Fragment { get; init; }
}
@brunolins16 brunolins16 added area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels feature-minimal-actions Controller-like actions for endpoint routing labels Mar 21, 2022
@rafikiassumani-msft rafikiassumani-msft added this to the 7.0-preview4 milestone Mar 21, 2022
@brunolins16 brunolins16 added api-suggestion Early API idea and discussion, it is NOT ready for implementation 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 Mar 23, 2022
@ghost
Copy link

ghost commented Mar 24, 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.

@halter73
Copy link
Member

API Review:

  1. We're unsure about Instance vs. Default vs. Empty. Are we going to need this at all if Results.Typed.... returns a strongly typed shared instance? We think this should wait.
  2. We want to constructors, but we want to be more consistent with default values vs. overloads vs. init-only properties.

@brunolins16
Copy link
Member Author

@halter73 for those two types:

public class NoContentHttpResult : IResult
{
+    public static NoContentHttpResult Instance { get; } = new();
}

public class UnauthorizedHttpResult : IResult
{
+    public static UnauthorizedHttpResult Instance { get; } = new();
}

Should they have the Instance property or the default ctor?

@halter73
Copy link
Member

If we decide to add constructors to any of these types, I think every one of them should have at least one constructor even if there's an equivalent Instance property that's more efficient. Most people aren't going to care about one extra allocation a request, so I don't think that's an excuse to make the API less consistent.

My thinking is we should get rid of the public Instance properties altogether and just return the shared instance from Results. and Results.Typed. methods.

@davidfowl
Copy link
Member

My thinking is we should get rid of the public Instance properties altogether and just return the shared instance from Results. and Results.Typed. methods.

Also the non typed ones. I guess I wasn't clear but I expected us to change the existing impl to cache.

@halter73
Copy link
Member

halter73 commented Apr 6, 2022

API Review:

On second thought, we think the Results.Typed methods described in #41009 will be sufficient. We can always add the constructors later.

@halter73 halter73 closed this as completed Apr 6, 2022
@DamianEdwards DamianEdwards removed the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Apr 11, 2022
@ghost ghost locked as resolved and limited conversation to collaborators May 11, 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
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc area-web-frameworks *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels feature-minimal-actions Controller-like actions for endpoint routing
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants