From 0ab4de9035154681e2e1b41e11b30f0287184a1a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 9 Mar 2022 09:38:32 -0800 Subject: [PATCH 01/29] StatuCodeResult as base class --- src/Http/Http.Results/src/ContentResult.cs | 39 ++++----- src/Http/Http.Results/src/JsonResult.cs | 66 ++++----------- src/Http/Http.Results/src/ObjectResult.cs | 80 +++++++++++-------- src/Http/Http.Results/src/StatusCodeResult.cs | 23 ++++-- 4 files changed, 94 insertions(+), 114 deletions(-) diff --git a/src/Http/Http.Results/src/ContentResult.cs b/src/Http/Http.Results/src/ContentResult.cs index 4630b5945633..d9d2a6d5bf67 100644 --- a/src/Http/Http.Results/src/ContentResult.cs +++ b/src/Http/Http.Results/src/ContentResult.cs @@ -6,13 +6,20 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed partial class ContentResult : IResult +/// +/// +/// +public sealed partial class ContentHttpResult : StatusCodeHttpResult { private const string DefaultContentType = "text/plain; charset=utf-8"; private static readonly Encoding DefaultEncoding = Encoding.UTF8; + internal ContentHttpResult() + { + } + /// /// Gets or set the content representing the body of the response. /// @@ -23,37 +30,25 @@ internal sealed partial class ContentResult : IResult /// public string? ContentType { get; init; } - /// - /// Gets or sets the HTTP status code. - /// - public int? StatusCode { get; init; } - /// /// Writes the content to the HTTP response. /// /// The for the current request. /// A task that represents the asynchronous execute operation. - public async Task ExecuteAsync(HttpContext httpContext) + internal override async Task WriteContentAsync(HttpContext httpContext) { var response = httpContext.Response; - ResponseContentTypeHelper.ResolveContentTypeAndEncoding( - ContentType, - response.ContentType, - (DefaultContentType, DefaultEncoding), - ResponseContentTypeHelper.GetEncoding, - out var resolvedContentType, - out var resolvedContentTypeEncoding); + ContentType, + response.ContentType, + (DefaultContentType, DefaultEncoding), + ResponseContentTypeHelper.GetEncoding, + out var resolvedContentType, + out var resolvedContentTypeEncoding); response.ContentType = resolvedContentType; - if (StatusCode != null) - { - response.StatusCode = StatusCode.Value; - } - - var logger = httpContext.RequestServices.GetRequiredService>(); - + var logger = httpContext.RequestServices.GetRequiredService>(); Log.ContentResultExecuting(logger, resolvedContentType); if (Content != null) diff --git a/src/Http/Http.Results/src/JsonResult.cs b/src/Http/Http.Results/src/JsonResult.cs index d2dd5920d5f3..e5595da180e8 100644 --- a/src/Http/Http.Results/src/JsonResult.cs +++ b/src/Http/Http.Results/src/JsonResult.cs @@ -2,20 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Http.Result; /// /// An action result which formats the given object as JSON. /// -internal sealed partial class JsonResult : IResult +internal sealed partial class JsonHttpResult : ObjectHttpResult { - /// - /// Gets or sets the representing the Content-Type header of the response. - /// - public string? ContentType { get; init; } + public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) + : base(value) + { + JsonSerializerOptions = jsonSerializerOptions; + } + + public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) + : base(value, statusCode) + { + JsonSerializerOptions = jsonSerializerOptions; + } /// /// Gets or sets the serializer settings. @@ -28,49 +33,6 @@ internal sealed partial class JsonResult : IResult /// public JsonSerializerOptions? JsonSerializerOptions { get; init; } - /// - /// Gets or sets the HTTP status code. - /// - public int? StatusCode { get; init; } - - /// - /// Gets or sets the value to be formatted. - /// - public object? Value { get; init; } - - /// - /// Write the result as JSON to the HTTP response. - /// - /// The for the current request. - /// A task that represents the asynchronous execute operation. - Task IResult.ExecuteAsync(HttpContext httpContext) - { - var logger = httpContext.RequestServices.GetRequiredService>(); - Log.JsonResultExecuting(logger, Value); - - if (StatusCode is int statusCode) - { - httpContext.Response.StatusCode = statusCode; - } - - return httpContext.Response.WriteAsJsonAsync(Value, JsonSerializerOptions, ContentType); - } - - private static partial class Log - { - public static void JsonResultExecuting(ILogger logger, object? value) - { - if (logger.IsEnabled(LogLevel.Information)) - { - var type = value == null ? "null" : value.GetType().FullName!; - JsonResultExecuting(logger, type); - } - } - - [LoggerMessage(1, LogLevel.Information, - "Executing JsonResult, writing value of type '{Type}'.", - EventName = "JsonResultExecuting", - SkipEnabledCheck = true)] - private static partial void JsonResultExecuting(ILogger logger, string type); - } + protected internal override Task WriteHttpResultAsync(HttpContext httpContext) + => httpContext.Response.WriteAsJsonAsync(Value, JsonSerializerOptions, ContentType); } diff --git a/src/Http/Http.Results/src/ObjectResult.cs b/src/Http/Http.Results/src/ObjectResult.cs index 3204d866b558..de40df18907d 100644 --- a/src/Http/Http.Results/src/ObjectResult.cs +++ b/src/Http/Http.Results/src/ObjectResult.cs @@ -6,25 +6,37 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal partial class ObjectResult : IResult +/// +/// +/// +public abstract partial class ObjectHttpResult : StatusCodeHttpResult { /// - /// Creates a new instance with the provided . + /// Creates a new instance with the provided . /// - public ObjectResult(object? value) + internal ObjectHttpResult(object? value) + : this(value, null) { - Value = value; } /// - /// Creates a new instance with the provided . + /// Creates a new instance with the provided and a specific . /// - public ObjectResult(object? value, int? statusCode) + internal ObjectHttpResult(object? value, int? statusCode) { Value = value; - StatusCode = statusCode; + + if (Value is ProblemDetails problemDetails) + { + statusCode = ApplyProblemDetailsDefaults(problemDetails, statusCode); + } + + if (statusCode is { } status) + { + StatusCode = status; + } } /// @@ -32,32 +44,17 @@ public ObjectResult(object? value, int? statusCode) /// public object? Value { get; } - /// - /// Gets the HTTP status code. - /// - public int? StatusCode { get; set; } - /// /// Gets the value for the Content-Type header. /// public string? ContentType { get; set; } - public Task ExecuteAsync(HttpContext httpContext) + internal override Task WriteContentAsync(HttpContext httpContext) { var loggerFactory = httpContext.RequestServices.GetRequiredService(); var logger = loggerFactory.CreateLogger(GetType()); Log.ObjectResultExecuting(logger, Value, StatusCode); - if (Value is ProblemDetails problemDetails) - { - ApplyProblemDetailsDefaults(problemDetails); - } - - if (StatusCode is { } statusCode) - { - httpContext.Response.StatusCode = statusCode; - } - ConfigureResponseHeaders(httpContext); if (Value is null) @@ -66,27 +63,43 @@ public Task ExecuteAsync(HttpContext httpContext) } OnFormatting(httpContext); - return httpContext.Response.WriteAsJsonAsync(Value, Value.GetType(), options: null, contentType: ContentType); + return WriteHttpResultAsync(httpContext); } - protected virtual void OnFormatting(HttpContext httpContext) + /// + /// + /// + /// + protected internal virtual void OnFormatting(HttpContext httpContext) { } - protected virtual void ConfigureResponseHeaders(HttpContext httpContext) + /// + /// + /// + /// + protected internal virtual void ConfigureResponseHeaders(HttpContext httpContext) { } - private void ApplyProblemDetailsDefaults(ProblemDetails problemDetails) + /// + /// + /// + /// + /// + protected internal virtual Task WriteHttpResultAsync(HttpContext httpContext) + => httpContext.Response.WriteAsJsonAsync(Value, Value!.GetType(), options: null, contentType: ContentType); + + private static int? ApplyProblemDetailsDefaults(ProblemDetails problemDetails, int? statusCode) { // We allow StatusCode to be specified either on ProblemDetails or on the ObjectResult and use it to configure the other. // This lets users write return Conflict(new Problem("some description")) // or return Problem("some-problem", 422) and have the response have consistent fields. if (problemDetails.Status is null) { - if (StatusCode is not null) + if (statusCode is not null) { - problemDetails.Status = StatusCode; + problemDetails.Status = statusCode; } else { @@ -96,16 +109,13 @@ private void ApplyProblemDetailsDefaults(ProblemDetails problemDetails) } } - if (StatusCode is null) - { - StatusCode = problemDetails.Status; - } - if (ProblemDetailsDefaults.Defaults.TryGetValue(problemDetails.Status.Value, out var defaults)) { problemDetails.Title ??= defaults.Title; problemDetails.Type ??= defaults.Type; } + + return statusCode ?? problemDetails.Status; } private static partial class Log diff --git a/src/Http/Http.Results/src/StatusCodeResult.cs b/src/Http/Http.Results/src/StatusCodeResult.cs index 2140eb5cc30f..c6a2794ba67d 100644 --- a/src/Http/Http.Results/src/StatusCodeResult.cs +++ b/src/Http/Http.Results/src/StatusCodeResult.cs @@ -4,16 +4,24 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal partial class StatusCodeResult : IResult +/// +/// +/// +public partial class StatusCodeHttpResult : IResult { + internal StatusCodeHttpResult() + : this(StatusCodes.Status200OK) + { + } + /// /// Initializes a new instance of the class /// with the given . /// /// The HTTP status code of the response. - public StatusCodeResult(int statusCode) + protected StatusCodeHttpResult(int statusCode) { StatusCode = statusCode; } @@ -21,7 +29,7 @@ public StatusCodeResult(int statusCode) /// /// Gets the HTTP status code. /// - public int StatusCode { get; } + public int StatusCode { get; init; } /// /// Sets the status code on the HTTP response. @@ -34,8 +42,13 @@ public Task ExecuteAsync(HttpContext httpContext) var logger = factory.CreateLogger(GetType()); Log.StatusCodeResultExecuting(logger, StatusCode); - httpContext.Response.StatusCode = StatusCode; + + return WriteContentAsync(httpContext); + } + + internal virtual Task WriteContentAsync(HttpContext httpContext) + { return Task.CompletedTask; } From 813786eba7223495388e09bc71ac2071e07893b0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 9 Mar 2022 09:44:12 -0800 Subject: [PATCH 02/29] StatuCodeResult as base class --- src/Http/Http.Results/src/ContentResult.cs | 12 ++++++------ src/Http/Http.Results/src/JsonResult.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Http/Http.Results/src/ContentResult.cs b/src/Http/Http.Results/src/ContentResult.cs index d9d2a6d5bf67..7244999c7713 100644 --- a/src/Http/Http.Results/src/ContentResult.cs +++ b/src/Http/Http.Results/src/ContentResult.cs @@ -39,12 +39,12 @@ internal override async Task WriteContentAsync(HttpContext httpContext) { var response = httpContext.Response; ResponseContentTypeHelper.ResolveContentTypeAndEncoding( - ContentType, - response.ContentType, - (DefaultContentType, DefaultEncoding), - ResponseContentTypeHelper.GetEncoding, - out var resolvedContentType, - out var resolvedContentTypeEncoding); + ContentType, + response.ContentType, + (DefaultContentType, DefaultEncoding), + ResponseContentTypeHelper.GetEncoding, + out var resolvedContentType, + out var resolvedContentTypeEncoding); response.ContentType = resolvedContentType; diff --git a/src/Http/Http.Results/src/JsonResult.cs b/src/Http/Http.Results/src/JsonResult.cs index e5595da180e8..d7a8e7b38234 100644 --- a/src/Http/Http.Results/src/JsonResult.cs +++ b/src/Http/Http.Results/src/JsonResult.cs @@ -3,7 +3,7 @@ using System.Text.Json; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// /// An action result which formats the given object as JSON. From 811c60489181fab7a8366e98429388993e77345c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 10 Mar 2022 14:51:44 -0800 Subject: [PATCH 03/29] Making types public and renaming to HTTPResult --- .../src/AcceptedAtRouteHttpResult.cs | 33 ++++++ .../Http.Results/src/AcceptedAtRouteResult.cs | 66 ----------- .../Http.Results/src/AcceptedHttpResult.cs | 38 ++++++ ...esult.cs => BadRequestObjectHttpResult.cs} | 6 +- ...llengeResult.cs => ChallengeHttpResult.cs} | 37 +++--- ...tResult.cs => ConflictObjectHttpResult.cs} | 6 +- ...{ContentResult.cs => ContentHttpResult.cs} | 0 .../src/CreatedAtRouteHttpResult.cs | 33 ++++++ .../Http.Results/src/CreatedHttpResult.cs | 28 +++++ src/Http/Http.Results/src/CreatedResult.cs | 54 --------- .../{EmptyResult.cs => EmptyHttpResult.cs} | 7 +- ...tentResult.cs => FileContentHttpResult.cs} | 14 +-- .../{FileResultBase.cs => FileHttpResult.cs} | 94 ++++++++++++--- src/Http/Http.Results/src/FileResult.cs | 62 ---------- ...treamResult.cs => FileStreamHttpResult.cs} | 16 +-- .../{ForbidResult.cs => ForbidHttpResult.cs} | 30 ++--- .../src/{JsonResult.cs => JsonHttpResult.cs} | 0 .../src/LocalRedirectHttpResult.cs | 45 ++++++++ .../Http.Results/src/LocalRedirectResult.cs | 108 ------------------ .../Http.Results/src/NoContentHttpResult.cs | 12 ++ src/Http/Http.Results/src/NoContentResult.cs | 11 -- ...tResult.cs => NotFoundObjectHttpResult.cs} | 6 +- ...esult.cs => ObjectAtLocationHttpResult.cs} | 43 +++---- ...teResult.cs => ObjectAtRouteHttpResult.cs} | 35 +++--- .../{ObjectResult.cs => ObjectHttpResult.cs} | 18 +-- ...kObjectResult.cs => OkObjectHttpResult.cs} | 6 +- ...ileResult.cs => PhysicalFileHttpResult.cs} | 16 +-- .../Http.Results/src/ProblemHttpResult.cs | 25 ++++ .../Http.Results/src/PublicAPI.Unshipped.txt | 69 +++++++++++ ...treamResult.cs => PushStreamHttpResult.cs} | 12 +- ...edirectResult.cs => RedirectHttpResult.cs} | 35 ++++-- ...Result.cs => RedirectToRouteHttpResult.cs} | 44 +++---- src/Http/Http.Results/src/Results.cs | 91 +++++++-------- .../{SignInResult.cs => SignInHttpResult.cs} | 28 ++--- ...{SignOutResult.cs => SignOutHttpResult.cs} | 30 ++--- ...sCodeResult.cs => StatusCodeHttpResult.cs} | 9 +- .../src/UnauthorizedHttpResult.cs | 12 ++ .../Http.Results/src/UnauthorizedResult.cs | 11 -- ...=> UnprocessableEntityObjectHttpResult.cs} | 6 +- ...FileResult.cs => VirtualFileHttpResult.cs} | 16 +-- .../test/AcceptedAtRouteResultTests.cs | 6 +- .../Http.Results/test/AcceptedResultTests.cs | 4 +- .../test/BadRequestObjectResultTests.cs | 2 +- .../Http.Results/test/ChallengeResultTest.cs | 4 +- .../test/ConflictObjectResultTest.cs | 2 +- .../Http.Results/test/ContentResultTest.cs | 5 +- .../test/CreatedAtRouteResultTests.cs | 4 +- .../Http.Results/test/CreatedResultTest.cs | 6 +- src/Http/Http.Results/test/EmptyResultTest.cs | 2 +- .../test/FileContentResultTest.cs | 2 +- .../Http.Results/test/FileStreamResultTest.cs | 8 +- .../Http.Results/test/ForbidResultTest.cs | 8 +- .../test/LocalRedirectResultTest.cs | 12 +- .../test/NotFoundObjectResultTest.cs | 6 +- .../Http.Results/test/ObjectResultTests.cs | 20 ++-- .../Http.Results/test/OkObjectResultTest.cs | 2 +- .../test/PhysicalFileResultTest.cs | 2 +- .../Http.Results/test/PushStreamResultTest.cs | 6 +- .../Http.Results/test/RedirectResultTest.cs | 4 +- .../test/RedirectToRouteResultTest.cs | 8 +- .../Http.Results/test/SignInResultTest.cs | 6 +- .../Http.Results/test/SignOutResultTest.cs | 6 +- .../test/StatusCodeResultTests.cs | 2 +- .../test/UnauthorizedResultTests.cs | 2 +- .../UnprocessableEntityObjectResultTests.cs | 2 +- .../test/VirtualFileResultTest.cs | 2 +- 66 files changed, 704 insertions(+), 641 deletions(-) create mode 100644 src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs delete mode 100644 src/Http/Http.Results/src/AcceptedAtRouteResult.cs create mode 100644 src/Http/Http.Results/src/AcceptedHttpResult.cs rename src/Http/Http.Results/src/{BadRequestObjectResult.cs => BadRequestObjectHttpResult.cs} (57%) rename src/Http/Http.Results/src/{ChallengeResult.cs => ChallengeHttpResult.cs} (72%) rename src/Http/Http.Results/src/{ConflictObjectResult.cs => ConflictObjectHttpResult.cs} (56%) rename src/Http/Http.Results/src/{ContentResult.cs => ContentHttpResult.cs} (100%) create mode 100644 src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs create mode 100644 src/Http/Http.Results/src/CreatedHttpResult.cs delete mode 100644 src/Http/Http.Results/src/CreatedResult.cs rename src/Http/Http.Results/src/{EmptyResult.cs => EmptyHttpResult.cs} (71%) rename src/Http/Http.Results/src/{FileContentResult.cs => FileContentHttpResult.cs} (67%) rename src/Http/Http.Results/src/{FileResultBase.cs => FileHttpResult.cs} (54%) delete mode 100644 src/Http/Http.Results/src/FileResult.cs rename src/Http/Http.Results/src/{FileStreamResult.cs => FileStreamHttpResult.cs} (73%) rename src/Http/Http.Results/src/{ForbidResult.cs => ForbidHttpResult.cs} (78%) rename src/Http/Http.Results/src/{JsonResult.cs => JsonHttpResult.cs} (100%) create mode 100644 src/Http/Http.Results/src/LocalRedirectHttpResult.cs delete mode 100644 src/Http/Http.Results/src/LocalRedirectResult.cs create mode 100644 src/Http/Http.Results/src/NoContentHttpResult.cs delete mode 100644 src/Http/Http.Results/src/NoContentResult.cs rename src/Http/Http.Results/src/{NotFoundObjectResult.cs => NotFoundObjectHttpResult.cs} (57%) rename src/Http/Http.Results/src/{AcceptedResult.cs => ObjectAtLocationHttpResult.cs} (52%) rename src/Http/Http.Results/src/{CreatedAtRouteResult.cs => ObjectAtRouteHttpResult.cs} (60%) rename src/Http/Http.Results/src/{ObjectResult.cs => ObjectHttpResult.cs} (89%) rename src/Http/Http.Results/src/{OkObjectResult.cs => OkObjectHttpResult.cs} (58%) rename src/Http/Http.Results/src/{PhysicalFileResult.cs => PhysicalFileHttpResult.cs} (82%) create mode 100644 src/Http/Http.Results/src/ProblemHttpResult.cs rename src/Http/Http.Results/src/{PushStreamResult.cs => PushStreamHttpResult.cs} (57%) rename src/Http/Http.Results/src/{RedirectResult.cs => RedirectHttpResult.cs} (58%) rename src/Http/Http.Results/src/{RedirectToRouteResult.cs => RedirectToRouteHttpResult.cs} (86%) rename src/Http/Http.Results/src/{SignInResult.cs => SignInHttpResult.cs} (76%) rename src/Http/Http.Results/src/{SignOutResult.cs => SignOutHttpResult.cs} (77%) rename src/Http/Http.Results/src/{StatusCodeResult.cs => StatusCodeHttpResult.cs} (85%) create mode 100644 src/Http/Http.Results/src/UnauthorizedHttpResult.cs delete mode 100644 src/Http/Http.Results/src/UnauthorizedResult.cs rename src/Http/Http.Results/src/{UnprocessableEntityObjectResult.cs => UnprocessableEntityObjectHttpResult.cs} (55%) rename src/Http/Http.Results/src/{VirtualFileResult.cs => VirtualFileHttpResult.cs} (81%) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs new file mode 100644 index 000000000000..e5fcc7dee48a --- /dev/null +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +internal sealed class AcceptedAtRouteHttpResult : ObjectAtRouteHttpResult +{ + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public AcceptedAtRouteHttpResult(object? routeValues, object? value) + : this(routeName: null, routeValues: routeValues, value: value) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public AcceptedAtRouteHttpResult( + string? routeName, + object? routeValues, + object? value) + : base(routeName, routeValues, value, StatusCodes.Status202Accepted) + { + } +} diff --git a/src/Http/Http.Results/src/AcceptedAtRouteResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteResult.cs deleted file mode 100644 index 1958f7d402fd..000000000000 --- a/src/Http/Http.Results/src/AcceptedAtRouteResult.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Http.Result; - -internal sealed class AcceptedAtRouteResult : ObjectResult -{ - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The route data to use for generating the URL. - /// The value to format in the entity body. - public AcceptedAtRouteResult(object? routeValues, object? value) - : this(routeName: null, routeValues: routeValues, value: value) - { - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The name of the route to use for generating the URL. - /// The route data to use for generating the URL. - /// The value to format in the entity body. - public AcceptedAtRouteResult( - string? routeName, - object? routeValues, - object? value) - : base(value, StatusCodes.Status202Accepted) - { - RouteName = routeName; - RouteValues = new RouteValueDictionary(routeValues); - } - - /// - /// Gets the name of the route to use for generating the URL. - /// - public string? RouteName { get; } - - /// - /// Gets the route data to use for generating the URL. - /// - public RouteValueDictionary RouteValues { get; } - - /// - protected override void ConfigureResponseHeaders(HttpContext context) - { - var linkGenerator = context.RequestServices.GetRequiredService(); - var url = linkGenerator.GetUriByAddress( - context, - RouteName, - RouteValues, - fragment: FragmentString.Empty); - - if (string.IsNullOrEmpty(url)) - { - throw new InvalidOperationException("No route matches the supplied values."); - } - - context.Response.Headers.Location = url; - } -} diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs new file mode 100644 index 000000000000..393aa6d0caee --- /dev/null +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +internal sealed class AcceptedHttpResult : ObjectAtLocationHttpResult +{ + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + public AcceptedHttpResult() + : base(location: null, value: null, StatusCodes.Status202Accepted) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The location at which the status of requested content can be monitored. + /// The value to format in the entity body. + public AcceptedHttpResult(string? location, object? value) + : base(location, value, StatusCodes.Status202Accepted) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The location at which the status of requested content can be monitored. + /// The value to format in the entity body. + public AcceptedHttpResult(Uri locationUri, object? value) + : base(locationUri, value, StatusCodes.Status202Accepted) + { + } +} diff --git a/src/Http/Http.Results/src/BadRequestObjectResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs similarity index 57% rename from src/Http/Http.Results/src/BadRequestObjectResult.cs rename to src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index 7f58b7c9d6bc..ebd2e2329243 100644 --- a/src/Http/Http.Results/src/BadRequestObjectResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class BadRequestObjectResult : ObjectResult +internal sealed class BadRequestObjectHttpResult : ObjectHttpResult { - public BadRequestObjectResult(object? error) + public BadRequestObjectHttpResult(object? error) : base(error, StatusCodes.Status400BadRequest) { } diff --git a/src/Http/Http.Results/src/ChallengeResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs similarity index 72% rename from src/Http/Http.Results/src/ChallengeResult.cs rename to src/Http/Http.Results/src/ChallengeHttpResult.cs index a0aa35ae28fe..2abbe533da5b 100644 --- a/src/Http/Http.Results/src/ChallengeResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -6,84 +6,91 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// /// An that on execution invokes . /// -internal sealed partial class ChallengeResult : IResult +public sealed partial class ChallengeHttpResult : IResult { /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - public ChallengeResult() + internal ChallengeHttpResult() : this(Array.Empty()) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme. /// /// The authentication scheme to challenge. - public ChallengeResult(string authenticationScheme) + internal ChallengeHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication schemes. /// /// The authentication schemes to challenge. - public ChallengeResult(IList authenticationSchemes) + internal ChallengeHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified . /// /// used to perform the authentication /// challenge. - public ChallengeResult(AuthenticationProperties? properties) + internal ChallengeHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme and . /// /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - public ChallengeResult(string authenticationScheme, AuthenticationProperties? properties) + internal ChallengeHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication schemes and . /// /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - public ChallengeResult(IList authenticationSchemes, AuthenticationProperties? properties) + internal ChallengeHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes; Properties = properties; } + /// + /// Gets or sets the authentication schemes that are challenged. + /// public IList AuthenticationSchemes { get; init; } = Array.Empty(); + /// + /// Gets or sets the used to perform the sign-out operation. + /// public AuthenticationProperties? Properties { get; init; } + /// public async Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + var logger = httpContext.RequestServices.GetRequiredService>(); Log.ChallengeResultExecuting(logger, AuthenticationSchemes); diff --git a/src/Http/Http.Results/src/ConflictObjectResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs similarity index 56% rename from src/Http/Http.Results/src/ConflictObjectResult.cs rename to src/Http/Http.Results/src/ConflictObjectHttpResult.cs index 68308b14d2eb..8250b90d9ef5 100644 --- a/src/Http/Http.Results/src/ConflictObjectResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class ConflictObjectResult : ObjectResult +internal sealed class ConflictObjectHttpResult : ObjectHttpResult { - public ConflictObjectResult(object? error) : + public ConflictObjectHttpResult(object? error) : base(error, StatusCodes.Status409Conflict) { } diff --git a/src/Http/Http.Results/src/ContentResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs similarity index 100% rename from src/Http/Http.Results/src/ContentResult.cs rename to src/Http/Http.Results/src/ContentHttpResult.cs diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs new file mode 100644 index 000000000000..42d9093a6ffb --- /dev/null +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +internal sealed class CreatedAtRouteHttpResult : ObjectAtRouteHttpResult +{ + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public CreatedAtRouteHttpResult(object? routeValues, object? value) + : this(routeName: null, routeValues: routeValues, value: value) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public CreatedAtRouteHttpResult( + string? routeName, + object? routeValues, + object? value) + : base(routeName, routeValues, value, StatusCodes.Status201Created) + { + } +} diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs new file mode 100644 index 000000000000..5d6f6adc5d64 --- /dev/null +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +internal sealed class CreatedHttpResult : ObjectAtLocationHttpResult +{ + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The location at which the content has been created. + /// The value to format in the entity body. + public CreatedHttpResult(string location, object? value) + : base(location, value, StatusCodes.Status201Created) + { } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The location at which the content has been created. + /// The value to format in the entity body. + public CreatedHttpResult(Uri location, object? value) + : base(location, value, StatusCodes.Status201Created) + { + } +} diff --git a/src/Http/Http.Results/src/CreatedResult.cs b/src/Http/Http.Results/src/CreatedResult.cs deleted file mode 100644 index 78cc9a361bc0..000000000000 --- a/src/Http/Http.Results/src/CreatedResult.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http.Result; - -internal sealed class CreatedResult : ObjectResult -{ - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The location at which the content has been created. - /// The value to format in the entity body. - public CreatedResult(string location, object? value) - : base(value, StatusCodes.Status201Created) - { - Location = location; - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The location at which the content has been created. - /// The value to format in the entity body. - public CreatedResult(Uri location, object? value) - : base(value, StatusCodes.Status201Created) - { - if (location == null) - { - throw new ArgumentNullException(nameof(location)); - } - - if (location.IsAbsoluteUri) - { - Location = location.AbsoluteUri; - } - else - { - Location = location.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); - } - } - - /// - /// Gets or sets the location at which the content has been created. - /// - public string Location { get; init; } - - /// - protected override void ConfigureResponseHeaders(HttpContext context) - { - context.Response.Headers.Location = Location; - } -} diff --git a/src/Http/Http.Results/src/EmptyResult.cs b/src/Http/Http.Results/src/EmptyHttpResult.cs similarity index 71% rename from src/Http/Http.Results/src/EmptyResult.cs rename to src/Http/Http.Results/src/EmptyHttpResult.cs index 739fa5783a34..661b5577615b 100644 --- a/src/Http/Http.Results/src/EmptyResult.cs +++ b/src/Http/Http.Results/src/EmptyHttpResult.cs @@ -7,14 +7,15 @@ namespace Microsoft.AspNetCore.Http; /// Represents an that when executed will /// do nothing. /// -internal sealed class EmptyResult : IResult +public sealed class EmptyHttpResult : IResult { - internal static readonly EmptyResult Instance = new(); + internal static readonly EmptyHttpResult Instance = new(); - private EmptyResult() + private EmptyHttpResult() { } + /// public Task ExecuteAsync(HttpContext httpContext) { return Task.CompletedTask; diff --git a/src/Http/Http.Results/src/FileContentResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs similarity index 67% rename from src/Http/Http.Results/src/FileContentResult.cs rename to src/Http/Http.Results/src/FileContentHttpResult.cs index f4a81e92a3c4..1f2a8a1b2ec4 100644 --- a/src/Http/Http.Results/src/FileContentResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -6,18 +6,18 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed partial class FileContentResult : FileResult +internal sealed partial class FileContentHttpResult : FileHttpResult { /// - /// Creates a new instance with + /// Creates a new instance with /// the provided and the /// provided . /// /// The bytes that represent the file contents. /// The Content-Type header of the response. - public FileContentResult(ReadOnlyMemory fileContents, string? contentType) + public FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) : base(contentType) { FileContents = fileContents; @@ -29,12 +29,12 @@ public FileContentResult(ReadOnlyMemory fileContents, string? contentType) /// public ReadOnlyMemory FileContents { get; init; } - protected override ILogger GetLogger(HttpContext httpContext) + protected internal override ILogger GetLogger(HttpContext httpContext) { - return httpContext.RequestServices.GetRequiredService>(); + return httpContext.RequestServices.GetRequiredService>(); } - protected override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) { return FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength); } diff --git a/src/Http/Http.Results/src/FileResultBase.cs b/src/Http/Http.Results/src/FileHttpResult.cs similarity index 54% rename from src/Http/Http.Results/src/FileResultBase.cs rename to src/Http/Http.Results/src/FileHttpResult.cs index a5cdeb88f9de..01c10808d6ba 100644 --- a/src/Http/Http.Results/src/FileResultBase.cs +++ b/src/Http/Http.Results/src/FileHttpResult.cs @@ -2,21 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Internal; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal abstract partial class FileResultBase +/// +/// Represents an that when executed will +/// write a file as the response. +/// +public abstract partial class FileHttpResult : IResult { private string? _fileDownloadName; - /// - /// Creates a new instance with - /// the provided . - /// - /// The Content-Type header of the response. - protected FileResultBase(string? contentType) + internal FileHttpResult(string? contentType) { ContentType = contentType ?? "application/octet-stream"; } @@ -37,25 +37,91 @@ public string FileDownloadName } /// - /// Gets or sets the last modified information associated with the . + /// Gets or sets the last modified information associated with the . /// public DateTimeOffset? LastModified { get; set; } /// - /// Gets or sets the etag associated with the . + /// Gets or sets the etag associated with the . /// public EntityTagHeaderValue? EntityTag { get; init; } /// - /// Gets or sets the value that enables range processing for the . + /// Gets or sets the value that enables range processing for the . /// public bool EnableRangeProcessing { get; init; } + /// + /// Gets or sets the file length information associated with the . + /// public long? FileLength { get; set; } - protected static partial class Log + /// + /// + /// + /// + /// + protected internal abstract ILogger GetLogger(HttpContext httpContext); + + /// + /// + /// + /// + /// + /// + /// + protected internal abstract Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength); + + /// + /// + /// + /// + /// + public virtual Task ExecuteAsync(HttpContext httpContext) + { + var logger = GetLogger(httpContext); + + Log.ExecutingFileResult(logger, this); + + var fileResultInfo = new FileResultInfo + { + ContentType = ContentType, + EnableRangeProcessing = EnableRangeProcessing, + EntityTag = EntityTag, + FileDownloadName = FileDownloadName, + LastModified = LastModified, + }; + + var (range, rangeLength, serveBody) = FileResultHelper.SetHeadersAndLog( + httpContext, + fileResultInfo, + FileLength, + EnableRangeProcessing, + LastModified, + EntityTag, + logger); + + if (!serveBody) + { + return Task.CompletedTask; + } + + if (range != null && rangeLength == 0) + { + return Task.CompletedTask; + } + + if (range != null) + { + FileResultHelper.Log.WritingRangeToBody(logger); + } + + return ExecuteCoreAsync(httpContext, range, rangeLength); + } + + internal static partial class Log { - public static void ExecutingFileResult(ILogger logger, FileResultBase fileResult) + public static void ExecutingFileResult(ILogger logger, FileHttpResult fileResult) { if (logger.IsEnabled(LogLevel.Information)) { @@ -64,7 +130,7 @@ public static void ExecutingFileResult(ILogger logger, FileResultBase fileResult } } - public static void ExecutingFileResult(ILogger logger, FileResultBase fileResult, string fileName) + public static void ExecutingFileResult(ILogger logger, FileHttpResult fileResult, string fileName) { if (logger.IsEnabled(LogLevel.Information)) { diff --git a/src/Http/Http.Results/src/FileResult.cs b/src/Http/Http.Results/src/FileResult.cs deleted file mode 100644 index a65bd86c82d3..000000000000 --- a/src/Http/Http.Results/src/FileResult.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Internal; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; - -namespace Microsoft.AspNetCore.Http.Result; - -internal abstract class FileResult : FileResultBase, IResult -{ - public FileResult(string? contentType) - : base(contentType) - { - } - - protected abstract ILogger GetLogger(HttpContext httpContext); - - protected abstract Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength); - - public virtual Task ExecuteAsync(HttpContext httpContext) - { - var logger = GetLogger(httpContext); - - Log.ExecutingFileResult(logger, this); - - var fileResultInfo = new FileResultInfo - { - ContentType = ContentType, - EnableRangeProcessing = EnableRangeProcessing, - EntityTag = EntityTag, - FileDownloadName = FileDownloadName, - LastModified = LastModified, - }; - - var (range, rangeLength, serveBody) = FileResultHelper.SetHeadersAndLog( - httpContext, - fileResultInfo, - FileLength, - EnableRangeProcessing, - LastModified, - EntityTag, - logger); - - if (!serveBody) - { - return Task.CompletedTask; - } - - if (range != null && rangeLength == 0) - { - return Task.CompletedTask; - } - - if (range != null) - { - FileResultHelper.Log.WritingRangeToBody(logger); - } - - return ExecuteCoreAsync(httpContext, range, rangeLength); - } -} diff --git a/src/Http/Http.Results/src/FileStreamResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs similarity index 73% rename from src/Http/Http.Results/src/FileStreamResult.cs rename to src/Http/Http.Results/src/FileStreamHttpResult.cs index ce196d18a009..6ba2561b1a7f 100644 --- a/src/Http/Http.Results/src/FileStreamResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -6,22 +6,22 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// -/// Represents an that when executed will +/// Represents an that when executed will /// write a file from a stream to the response. /// -internal sealed class FileStreamResult : FileResult, IResult +internal sealed class FileStreamHttpResult : FileHttpResult, IResult { /// - /// Creates a new instance with + /// Creates a new instance with /// the provided and the /// provided . /// /// The stream with the file. /// The Content-Type header of the response. - public FileStreamResult(Stream fileStream, string? contentType) + public FileStreamHttpResult(Stream fileStream, string? contentType) : base(contentType) { if (fileStream == null) @@ -41,9 +41,9 @@ public FileStreamResult(Stream fileStream, string? contentType) /// public Stream FileStream { get; } - protected override ILogger GetLogger(HttpContext httpContext) + protected internal override ILogger GetLogger(HttpContext httpContext) { - return httpContext.RequestServices.GetRequiredService>(); + return httpContext.RequestServices.GetRequiredService>(); } public override async Task ExecuteAsync(HttpContext httpContext) @@ -54,7 +54,7 @@ public override async Task ExecuteAsync(HttpContext httpContext) } } - protected override Task ExecuteCoreAsync(HttpContext context, RangeItemHeaderValue? range, long rangeLength) + protected internal override Task ExecuteCoreAsync(HttpContext context, RangeItemHeaderValue? range, long rangeLength) { return FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength); } diff --git a/src/Http/Http.Results/src/ForbidResult.cs b/src/Http/Http.Results/src/ForbidHttpResult.cs similarity index 78% rename from src/Http/Http.Results/src/ForbidResult.cs rename to src/Http/Http.Results/src/ForbidHttpResult.cs index fe66fce56ac0..fd3d0676df34 100644 --- a/src/Http/Http.Results/src/ForbidResult.cs +++ b/src/Http/Http.Results/src/ForbidHttpResult.cs @@ -6,69 +6,69 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed partial class ForbidResult : IResult +internal sealed partial class ForbidHttpResult : IResult { /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - public ForbidResult() + public ForbidHttpResult() : this(Array.Empty()) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme. /// /// The authentication scheme to challenge. - public ForbidResult(string authenticationScheme) + public ForbidHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication schemes. /// /// The authentication schemes to challenge. - public ForbidResult(IList authenticationSchemes) + public ForbidHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified . /// /// used to perform the authentication /// challenge. - public ForbidResult(AuthenticationProperties? properties) + public ForbidHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme and . /// /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - public ForbidResult(string authenticationScheme, AuthenticationProperties? properties) + public ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication schemes and . /// /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - public ForbidResult(IList authenticationSchemes, AuthenticationProperties? properties) + public ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes; Properties = properties; @@ -87,7 +87,7 @@ public ForbidResult(IList authenticationSchemes, AuthenticationPropertie /// public async Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + var logger = httpContext.RequestServices.GetRequiredService>(); Log.ForbidResultExecuting(logger, AuthenticationSchemes); diff --git a/src/Http/Http.Results/src/JsonResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs similarity index 100% rename from src/Http/Http.Results/src/JsonResult.cs rename to src/Http/Http.Results/src/JsonHttpResult.cs diff --git a/src/Http/Http.Results/src/LocalRedirectHttpResult.cs b/src/Http/Http.Results/src/LocalRedirectHttpResult.cs new file mode 100644 index 000000000000..1f4b2537e777 --- /dev/null +++ b/src/Http/Http.Results/src/LocalRedirectHttpResult.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), +/// or Permanent Redirect (308) response with a Location header to the supplied local URL. +/// +internal sealed partial class LocalRedirectHttpResult : RedirectHttpResult +{ + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The local URL to redirect to. + public LocalRedirectHttpResult(string localUrl) + : this(localUrl, permanent: false) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The local URL to redirect to. + /// Specifies whether the redirect should be permanent (301) or temporary (302). + public LocalRedirectHttpResult(string localUrl, bool permanent) + : this(localUrl, permanent, preserveMethod: false) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The local URL to redirect to. + /// Specifies whether the redirect should be permanent (301) or temporary (302). + /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request's method. + public LocalRedirectHttpResult(string localUrl, bool permanent, bool preserveMethod) + : base(localUrl, permanent, preserveMethod, acceptLocalUrlOnly: true) + { + + } +} diff --git a/src/Http/Http.Results/src/LocalRedirectResult.cs b/src/Http/Http.Results/src/LocalRedirectResult.cs deleted file mode 100644 index 0cec3ab1529e..000000000000 --- a/src/Http/Http.Results/src/LocalRedirectResult.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Internal; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Http.Result; - -/// -/// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), -/// or Permanent Redirect (308) response with a Location header to the supplied local URL. -/// -internal sealed partial class LocalRedirectResult : IResult -{ - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The local URL to redirect to. - public LocalRedirectResult(string localUrl) - : this(localUrl, permanent: false) - { - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The local URL to redirect to. - /// Specifies whether the redirect should be permanent (301) or temporary (302). - public LocalRedirectResult(string localUrl, bool permanent) - : this(localUrl, permanent, preserveMethod: false) - { - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The local URL to redirect to. - /// Specifies whether the redirect should be permanent (301) or temporary (302). - /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request's method. - public LocalRedirectResult(string localUrl, bool permanent, bool preserveMethod) - { - if (string.IsNullOrEmpty(localUrl)) - { - throw new ArgumentException("Argument cannot be null or empty", nameof(localUrl)); - } - - Permanent = permanent; - PreserveMethod = preserveMethod; - Url = localUrl; - } - - /// - /// Gets or sets the value that specifies that the redirect should be permanent if true or temporary if false. - /// - public bool Permanent { get; } - - /// - /// Gets or sets an indication that the redirect preserves the initial request method. - /// - public bool PreserveMethod { get; } - - /// - /// Gets or sets the local URL to redirect to. - /// - public string Url { get; } - - /// - public Task ExecuteAsync(HttpContext httpContext) - { - if (!SharedUrlHelper.IsLocalUrl(Url)) - { - throw new InvalidOperationException("The supplied URL is not local. A URL with an absolute path is considered local if it does not have a host/authority part. URLs using virtual paths ('~/') are also local."); - } - - var destinationUrl = SharedUrlHelper.Content(httpContext, Url); - - // IsLocalUrl is called to handle URLs starting with '~/'. - var logger = httpContext.RequestServices.GetRequiredService>(); - - Log.LocalRedirectResultExecuting(logger, destinationUrl); - - if (PreserveMethod) - { - httpContext.Response.StatusCode = Permanent - ? StatusCodes.Status308PermanentRedirect - : StatusCodes.Status307TemporaryRedirect; - httpContext.Response.Headers.Location = destinationUrl; - } - else - { - httpContext.Response.Redirect(destinationUrl, Permanent); - } - - return Task.CompletedTask; - } - - private static partial class Log - { - [LoggerMessage(1, LogLevel.Information, - "Executing LocalRedirectResult, redirecting to {Destination}.", - EventName = "LocalRedirectResultExecuting")] - public static partial void LocalRedirectResultExecuting(ILogger logger, string destination); - } -} diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs new file mode 100644 index 000000000000..7f98c4c3b304 --- /dev/null +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +internal class NoContentHttpResult : StatusCodeHttpResult +{ + public NoContentHttpResult() + : base(StatusCodes.Status204NoContent) + { + } +} diff --git a/src/Http/Http.Results/src/NoContentResult.cs b/src/Http/Http.Results/src/NoContentResult.cs deleted file mode 100644 index 582484c406c0..000000000000 --- a/src/Http/Http.Results/src/NoContentResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http.Result; - -internal class NoContentResult : StatusCodeResult -{ - public NoContentResult() : base(StatusCodes.Status204NoContent) - { - } -} diff --git a/src/Http/Http.Results/src/NotFoundObjectResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs similarity index 57% rename from src/Http/Http.Results/src/NotFoundObjectResult.cs rename to src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index 5ce0e6083bc9..cb02f7f9645e 100644 --- a/src/Http/Http.Results/src/NotFoundObjectResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class NotFoundObjectResult : ObjectResult +internal sealed class NotFoundObjectHttpResult : ObjectHttpResult { - public NotFoundObjectResult(object? value) + public NotFoundObjectHttpResult(object? value) : base(value, StatusCodes.Status404NotFound) { } diff --git a/src/Http/Http.Results/src/AcceptedResult.cs b/src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs similarity index 52% rename from src/Http/Http.Results/src/AcceptedResult.cs rename to src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs index 0f836f4db67b..9fb491934111 100644 --- a/src/Http/Http.Results/src/AcceptedResult.cs +++ b/src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs @@ -1,39 +1,42 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class AcceptedResult : ObjectResult +/// +/// An that on execution will write an object to the response +/// with a Location header to the supplied URL. +/// +public abstract class ObjectAtLocationHttpResult : ObjectHttpResult { /// - /// Initializes a new instance of the class with the values - /// provided. - /// - public AcceptedResult() - : base(value: null, StatusCodes.Status202Accepted) - { - } - - /// - /// Initializes a new instance of the class with the values + /// Initializes a new instance of the class with the values /// provided. /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - public AcceptedResult(string? location, object? value) - : base(value, StatusCodes.Status202Accepted) + /// The HTTP status code of the response. + internal ObjectAtLocationHttpResult( + string? location, + object? value, + int? statusCode) + : base(value, statusCode) { Location = location; } /// - /// Initializes a new instance of the class with the values + /// Initializes a new instance of the class with the values /// provided. /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - public AcceptedResult(Uri locationUri, object? value) - : base(value, StatusCodes.Status202Accepted) + /// The HTTP status code of the response. + internal ObjectAtLocationHttpResult( + Uri locationUri, + object? value, + int? statusCode) + : base(value, statusCode) { if (locationUri == null) { @@ -51,12 +54,12 @@ public AcceptedResult(Uri locationUri, object? value) } /// - /// Gets or sets the location at which the status of the requested content can be monitored. + /// Gets the location at which the status of the requested content can be monitored. /// - public string? Location { get; set; } + public string? Location { get; } /// - protected override void ConfigureResponseHeaders(HttpContext context) + protected internal override void ConfigureResponseHeaders(HttpContext context) { if (!string.IsNullOrEmpty(Location)) { diff --git a/src/Http/Http.Results/src/CreatedAtRouteResult.cs b/src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs similarity index 60% rename from src/Http/Http.Results/src/CreatedAtRouteResult.cs rename to src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs index 10b2fe9ab776..add99c920a56 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteResult.cs +++ b/src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs @@ -4,33 +4,28 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class CreatedAtRouteResult : ObjectResult +/// +/// An that on execution will write an object to the response +/// with a Location header. Targets a registered route. +/// +public abstract class ObjectAtRouteHttpResult : ObjectHttpResult { /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The route data to use for generating the URL. - /// The value to format in the entity body. - public CreatedAtRouteResult(object? routeValues, object? value) - : this(routeName: null, routeValues: routeValues, value: value) - { - } - - /// - /// Initializes a new instance of the class with the values + /// Initializes a new instance of the class with the values /// provided. /// /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - public CreatedAtRouteResult( + /// The HTTP status code of the response. + internal ObjectAtRouteHttpResult( string? routeName, object? routeValues, - object? value) - : base(value, StatusCodes.Status201Created) + object? value, + int? statusCode) + : base(value, statusCode) { RouteName = routeName; RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); @@ -39,15 +34,15 @@ public CreatedAtRouteResult( /// /// Gets or sets the name of the route to use for generating the URL. /// - public string? RouteName { get; set; } + public string? RouteName { get; } /// /// Gets or sets the route data to use for generating the URL. /// - public RouteValueDictionary? RouteValues { get; set; } + public RouteValueDictionary? RouteValues { get; } /// - protected override void ConfigureResponseHeaders(HttpContext context) + protected internal override void ConfigureResponseHeaders(HttpContext context) { var linkGenerator = context.RequestServices.GetRequiredService(); var url = linkGenerator.GetUriByRouteValues( diff --git a/src/Http/Http.Results/src/ObjectResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs similarity index 89% rename from src/Http/Http.Results/src/ObjectResult.cs rename to src/Http/Http.Results/src/ObjectHttpResult.cs index de40df18907d..cf4aa8128776 100644 --- a/src/Http/Http.Results/src/ObjectResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -9,12 +9,13 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that on execution will write an object to the response. /// -public abstract partial class ObjectHttpResult : StatusCodeHttpResult +public partial class ObjectHttpResult : StatusCodeHttpResult { /// - /// Creates a new instance with the provided . + /// Creates a new instance + /// with the provided . /// internal ObjectHttpResult(object? value) : this(value, null) @@ -22,7 +23,8 @@ internal ObjectHttpResult(object? value) } /// - /// Creates a new instance with the provided and a specific . + /// Creates a new instance with the provided + /// and . /// internal ObjectHttpResult(object? value, int? statusCode) { @@ -40,14 +42,14 @@ internal ObjectHttpResult(object? value, int? statusCode) } /// - /// The object result. + /// Gets or sets the object result. /// - public object? Value { get; } + public object? Value { get; init; } /// - /// Gets the value for the Content-Type header. + /// Gets or sets the value for the Content-Type header. /// - public string? ContentType { get; set; } + public string? ContentType { get; init; } internal override Task WriteContentAsync(HttpContext httpContext) { diff --git a/src/Http/Http.Results/src/OkObjectResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs similarity index 58% rename from src/Http/Http.Results/src/OkObjectResult.cs rename to src/Http/Http.Results/src/OkObjectHttpResult.cs index 70013671deea..ea97d7239d07 100644 --- a/src/Http/Http.Results/src/OkObjectResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class OkObjectResult : ObjectResult +internal sealed class OkObjectHttpResult : ObjectHttpResult { - public OkObjectResult(object? value) + public OkObjectHttpResult(object? value) : base(value, StatusCodes.Status200OK) { } diff --git a/src/Http/Http.Results/src/PhysicalFileResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs similarity index 82% rename from src/Http/Http.Results/src/PhysicalFileResult.cs rename to src/Http/Http.Results/src/PhysicalFileHttpResult.cs index 63d883ea6cdc..afbd653179f3 100644 --- a/src/Http/Http.Results/src/PhysicalFileResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -5,21 +5,21 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// -/// A on execution will write a file from disk to the response +/// A on execution will write a file from disk to the response /// using mechanisms provided by the host. /// -internal sealed partial class PhysicalFileResult : FileResult, IResult +internal sealed partial class PhysicalFileHttpResult : FileHttpResult, IResult { /// - /// Creates a new instance with + /// Creates a new instance with /// the provided and the provided . /// /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. - public PhysicalFileResult(string fileName, string? contentType) + public PhysicalFileHttpResult(string fileName, string? contentType) : base(contentType) { FileName = fileName; @@ -34,9 +34,9 @@ public PhysicalFileResult(string fileName, string? contentType) public Func GetFileInfoWrapper { get; init; } = static path => new FileInfoWrapper(path); - protected override ILogger GetLogger(HttpContext httpContext) + protected internal override ILogger GetLogger(HttpContext httpContext) { - return httpContext.RequestServices.GetRequiredService>(); + return httpContext.RequestServices.GetRequiredService>(); } public override Task ExecuteAsync(HttpContext httpContext) @@ -53,7 +53,7 @@ public override Task ExecuteAsync(HttpContext httpContext) return base.ExecuteAsync(httpContext); } - protected override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) { var response = httpContext.Response; if (!Path.IsPathRooted(FileName)) diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs new file mode 100644 index 000000000000..9e50d2720a00 --- /dev/null +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +using Microsoft.AspNetCore.Mvc; + +/// +/// An that on execution will write Problem Details +/// HTTP API responses based on https://tools.ietf.org/html/rfc7807 +/// +public sealed class ProblemHttpResult : ObjectHttpResult +{ + /// + /// Gets the instance. + /// + public ProblemDetails ProblemDetails { get; } + + internal ProblemHttpResult(ProblemDetails problemDetails) + : base(problemDetails) + { + ContentType = "application/problem+json"; + ProblemDetails = problemDetails; + } +} diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index aafb2178b5a7..92f0f6ebb0d1 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -1,5 +1,74 @@ #nullable enable +Microsoft.AspNetCore.Http.ChallengeHttpResult +Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.init -> void +Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.init -> void +Microsoft.AspNetCore.Http.ContentHttpResult +Microsoft.AspNetCore.Http.ContentHttpResult.Content.get -> string? +Microsoft.AspNetCore.Http.ContentHttpResult.Content.init -> void +Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.get -> string? +Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.init -> void +Microsoft.AspNetCore.Http.EmptyHttpResult +Microsoft.AspNetCore.Http.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.FileHttpResult +Microsoft.AspNetCore.Http.FileHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.FileHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.FileHttpResult.EnableRangeProcessing.init -> void +Microsoft.AspNetCore.Http.FileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.FileHttpResult.EntityTag.init -> void +Microsoft.AspNetCore.Http.FileHttpResult.FileDownloadName.get -> string! +Microsoft.AspNetCore.Http.FileHttpResult.FileDownloadName.init -> void +Microsoft.AspNetCore.Http.FileHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.FileHttpResult.FileLength.set -> void +Microsoft.AspNetCore.Http.FileHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.FileHttpResult.LastModified.set -> void +Microsoft.AspNetCore.Http.ObjectAtLocationHttpResult +Microsoft.AspNetCore.Http.ObjectAtLocationHttpResult.Location.get -> string? +Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult +Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult.RouteName.get -> string? +Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.ObjectHttpResult +Microsoft.AspNetCore.Http.ObjectHttpResult.ContentType.get -> string? +Microsoft.AspNetCore.Http.ObjectHttpResult.ContentType.init -> void +Microsoft.AspNetCore.Http.ObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.ObjectHttpResult.Value.init -> void +Microsoft.AspNetCore.Http.ProblemHttpResult +Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemDetails.get -> Microsoft.AspNetCore.Mvc.ProblemDetails! +Microsoft.AspNetCore.Http.RedirectHttpResult +Microsoft.AspNetCore.Http.RedirectHttpResult.AcceptLocalUrlOnly.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.RedirectHttpResult.Permanent.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string! +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Fragment.get -> string? +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Permanent.get -> bool +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteName.get -> string? +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.SignInHttpResult +Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.get -> string? +Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.init -> void +Microsoft.AspNetCore.Http.SignInHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.SignInHttpResult.Principal.get -> System.Security.Claims.ClaimsPrincipal! +Microsoft.AspNetCore.Http.SignInHttpResult.Principal.init -> void +Microsoft.AspNetCore.Http.SignInHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.SignInHttpResult.Properties.init -> void +Microsoft.AspNetCore.Http.SignOutHttpResult +Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.init -> void +Microsoft.AspNetCore.Http.SignOutHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.init -> void +Microsoft.AspNetCore.Http.StatusCodeHttpResult +Microsoft.AspNetCore.Http.StatusCodeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.get -> int +Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.init -> void static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, bool enableRangeProcessing = false, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.IO.Pipelines.PipeReader! pipeReader, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) -> Microsoft.AspNetCore.Http.IResult! +virtual Microsoft.AspNetCore.Http.FileHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! diff --git a/src/Http/Http.Results/src/PushStreamResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs similarity index 57% rename from src/Http/Http.Results/src/PushStreamResult.cs rename to src/Http/Http.Results/src/PushStreamHttpResult.cs index c498bf69d95b..2c310a9f5d79 100644 --- a/src/Http/Http.Results/src/PushStreamResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -5,24 +5,24 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class PushStreamResult : FileResult +internal sealed class PushStreamHttpResult : FileHttpResult { private readonly Func _streamWriterCallback; - public PushStreamResult(Func streamWriterCallback, string? contentType) + public PushStreamHttpResult(Func streamWriterCallback, string? contentType) : base(contentType) { _streamWriterCallback = streamWriterCallback; } - protected override ILogger GetLogger(HttpContext httpContext) + protected internal override ILogger GetLogger(HttpContext httpContext) { - return httpContext.RequestServices.GetRequiredService>(); + return httpContext.RequestServices.GetRequiredService>(); } - protected override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) { return _streamWriterCallback(httpContext.Response.Body); } diff --git a/src/Http/Http.Results/src/RedirectResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs similarity index 58% rename from src/Http/Http.Results/src/RedirectResult.cs rename to src/Http/Http.Results/src/RedirectHttpResult.cs index b5bdf7b1775d..16fd3565535c 100644 --- a/src/Http/Http.Results/src/RedirectResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -5,18 +5,23 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed partial class RedirectResult : IResult +/// +/// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), +/// or Permanent Redirect (308) response with a Location header to the supplied URL. +/// +public partial class RedirectHttpResult : IResult { /// - /// Initializes a new instance of the class with the values + /// Initializes a new instance of the class with the values /// provided. /// /// The URL to redirect to. /// Specifies whether the redirect should be permanent (301) or temporary (302). /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. - public RedirectResult(string url, bool permanent, bool preserveMethod) + /// If set to true, only local URLs are accepted and will throw an exception when the supplied URL is not considered local. (Default: false) + internal RedirectHttpResult(string url, bool permanent, bool preserveMethod, bool acceptLocalUrlOnly = false) { if (url == null) { @@ -30,31 +35,43 @@ public RedirectResult(string url, bool permanent, bool preserveMethod) Permanent = permanent; PreserveMethod = preserveMethod; + AcceptLocalUrlOnly = acceptLocalUrlOnly; Url = url; } /// - /// Gets or sets the value that specifies that the redirect should be permanent if true or temporary if false. + /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. /// public bool Permanent { get; } /// - /// Gets or sets an indication that the redirect preserves the initial request method. + /// Gets an indication that the redirect preserves the initial request method. /// public bool PreserveMethod { get; } /// - /// Gets or sets the URL to redirect to. + /// Gets the URL to redirect to. /// public string Url { get; } + /// + /// Gets an indication that only local URLs are accepted. + /// + public bool AcceptLocalUrlOnly { get; } + /// public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + var logger = httpContext.RequestServices.GetRequiredService>(); + var isLocalUrl = SharedUrlHelper.IsLocalUrl(Url); + + if (AcceptLocalUrlOnly && !isLocalUrl) + { + throw new InvalidOperationException("The supplied URL is not local. A URL with an absolute path is considered local if it does not have a host/authority part. URLs using virtual paths ('~/') are also local."); + } // IsLocalUrl is called to handle URLs starting with '~/'. - var destinationUrl = SharedUrlHelper.IsLocalUrl(Url) ? SharedUrlHelper.Content(httpContext, Url) : Url; + var destinationUrl = isLocalUrl ? SharedUrlHelper.Content(httpContext, Url) : Url; Log.RedirectResultExecuting(logger, destinationUrl); diff --git a/src/Http/Http.Results/src/RedirectToRouteResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs similarity index 86% rename from src/Http/Http.Results/src/RedirectToRouteResult.cs rename to src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index 1d7458fe6738..b00808b983a2 100644 --- a/src/Http/Http.Results/src/RedirectToRouteResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -5,32 +5,32 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// /// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), /// or Permanent Redirect (308) response with a Location header. /// Targets a registered route. /// -internal sealed partial class RedirectToRouteResult : IResult +public sealed partial class RedirectToRouteHttpResult : IResult { /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The parameters for the route. - public RedirectToRouteResult(object? routeValues) + internal RedirectToRouteHttpResult(object? routeValues) : this(routeName: null, routeValues: routeValues) { } /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The name of the route. /// The parameters for the route. - public RedirectToRouteResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues) : this(routeName, routeValues, permanent: false) @@ -38,13 +38,13 @@ public RedirectToRouteResult( } /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The name of the route. /// The parameters for the route. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). - public RedirectToRouteResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent) @@ -53,14 +53,14 @@ public RedirectToRouteResult( } /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The name of the route. /// The parameters for the route. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. - public RedirectToRouteResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -70,13 +70,13 @@ public RedirectToRouteResult( } /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The name of the route. /// The parameters for the route. /// The fragment to add to the URL. - public RedirectToRouteResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, string? fragment) @@ -85,14 +85,14 @@ public RedirectToRouteResult( } /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The name of the route. /// The parameters for the route. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// The fragment to add to the URL. - public RedirectToRouteResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -102,7 +102,7 @@ public RedirectToRouteResult( } /// - /// Initializes a new instance of the with the values + /// Initializes a new instance of the with the values /// provided. /// /// The name of the route. @@ -110,7 +110,7 @@ public RedirectToRouteResult( /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. /// The fragment to add to the URL. - public RedirectToRouteResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -125,27 +125,27 @@ public RedirectToRouteResult( } /// - /// Gets or sets the name of the route to use for generating the URL. + /// Gets the name of the route to use for generating the URL. /// public string? RouteName { get; } /// - /// Gets or sets the route data to use for generating the URL. + /// Gets the route data to use for generating the URL. /// public RouteValueDictionary? RouteValues { get; } /// - /// Gets or sets an indication that the redirect is permanent. + /// Gets an indication that the redirect is permanent. /// public bool Permanent { get; } /// - /// Gets or sets an indication that the redirect preserves the initial request method. + /// Gets an indication that the redirect preserves the initial request method. /// public bool PreserveMethod { get; } /// - /// Gets or sets the fragment to add to the URL. + /// Gets the fragment to add to the URL. /// public string? Fragment { get; } @@ -164,7 +164,7 @@ public Task ExecuteAsync(HttpContext httpContext) throw new InvalidOperationException("No route matches the supplied values."); } - var logger = httpContext.RequestServices.GetRequiredService>(); + var logger = httpContext.RequestServices.GetRequiredService>(); Log.RedirectToRouteResultExecuting(logger, destinationUrl, RouteName); if (PreserveMethod) diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 0f0328e76110..e5158c8e8eb8 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -7,7 +7,6 @@ using System.Text.Json; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http.Result; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; @@ -33,7 +32,7 @@ public static class Results public static IResult Challenge( AuthenticationProperties? properties = null, IList? authenticationSchemes = null) - => new ChallengeResult { AuthenticationSchemes = authenticationSchemes ?? Array.Empty(), Properties = properties }; + => new ChallengeHttpResult { AuthenticationSchemes = authenticationSchemes ?? Array.Empty(), Properties = properties }; /// /// Creates a that on execution invokes . @@ -51,7 +50,7 @@ public static IResult Challenge( /// a redirect to show a login page. /// public static IResult Forbid(AuthenticationProperties? properties = null, IList? authenticationSchemes = null) - => new ForbidResult { Properties = properties, AuthenticationSchemes = authenticationSchemes ?? Array.Empty(), }; + => new ForbidHttpResult { Properties = properties, AuthenticationSchemes = authenticationSchemes ?? Array.Empty(), }; /// /// Creates an that on execution invokes . @@ -64,7 +63,7 @@ public static IResult SignIn( ClaimsPrincipal principal, AuthenticationProperties? properties = null, string? authenticationScheme = null) - => new SignInResult(authenticationScheme, principal, properties); + => new SignInHttpResult(authenticationScheme, principal, properties); /// /// Creates an that on execution invokes . @@ -73,7 +72,7 @@ public static IResult SignIn( /// The authentication scheme to use for the sign-out operation. /// The created for the response. public static IResult SignOut(AuthenticationProperties? properties = null, IList? authenticationSchemes = null) - => new SignOutResult(authenticationSchemes ?? Array.Empty(), properties); + => new SignOutHttpResult(authenticationSchemes ?? Array.Empty(), properties); /// /// Writes the string to the HTTP response. @@ -115,7 +114,7 @@ public static IResult Text(string content, string? contentType = null, Encoding? mediaTypeHeaderValue.Encoding = contentEncoding ?? mediaTypeHeaderValue.Encoding; } - return new ContentResult + return new ContentHttpResult { Content = content, ContentType = mediaTypeHeaderValue?.ToString() @@ -129,7 +128,7 @@ public static IResult Text(string content, string? contentType = null, Encoding? /// The content type (MIME type). /// The created object for the response. public static IResult Content(string content, MediaTypeHeaderValue contentType) - => new ContentResult + => new ContentHttpResult { Content = content, ContentType = contentType.ToString() @@ -142,17 +141,14 @@ public static IResult Content(string content, MediaTypeHeaderValue contentType) /// The serializer options to use when serializing the value. /// The content-type to set on the response. /// The status code to set on the response. - /// The created that serializes the specified + /// The created that serializes the specified /// as JSON format for the response. /// Callers should cache an instance of serializer settings to avoid /// recreating cached data with each call. public static IResult Json(object? data, JsonSerializerOptions? options = null, string? contentType = null, int? statusCode = null) - => new JsonResult + => new JsonHttpResult(data, statusCode, options) { - Value = data, - JsonSerializerOptions = options, ContentType = contentType, - StatusCode = statusCode, }; /// @@ -180,7 +176,7 @@ public static IResult File( bool enableRangeProcessing = false, DateTimeOffset? lastModified = null, EntityTagHeaderValue? entityTag = null) - => new FileContentResult(fileContents, contentType) + => new FileContentHttpResult(fileContents, contentType) { FileDownloadName = fileDownloadName, EnableRangeProcessing = enableRangeProcessing, @@ -211,7 +207,7 @@ public static IResult Bytes( bool enableRangeProcessing = false, DateTimeOffset? lastModified = null, EntityTagHeaderValue? entityTag = null) - => new FileContentResult(contents, contentType) + => new FileContentHttpResult(contents, contentType) { FileDownloadName = fileDownloadName, EnableRangeProcessing = enableRangeProcessing, @@ -242,7 +238,7 @@ public static IResult Bytes( bool enableRangeProcessing = false, DateTimeOffset? lastModified = null, EntityTagHeaderValue? entityTag = null) - => new FileContentResult(contents, contentType) + => new FileContentHttpResult(contents, contentType) { FileDownloadName = fileDownloadName, EnableRangeProcessing = enableRangeProcessing, @@ -282,7 +278,7 @@ public static IResult File( EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) { - return new FileStreamResult(fileStream, contentType) + return new FileStreamHttpResult(fileStream, contentType) { LastModified = lastModified, EntityTag = entityTag, @@ -321,7 +317,7 @@ public static IResult Stream( EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) { - return new FileStreamResult(stream, contentType) + return new FileStreamHttpResult(stream, contentType) { LastModified = lastModified, EntityTag = entityTag, @@ -359,7 +355,7 @@ public static IResult Stream( EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) { - return new FileStreamResult(pipeReader.AsStream(), contentType) + return new FileStreamHttpResult(pipeReader.AsStream(), contentType) { LastModified = lastModified, EntityTag = entityTag, @@ -392,7 +388,7 @@ public static IResult Stream( DateTimeOffset? lastModified = null, EntityTagHeaderValue? entityTag = null) { - return new PushStreamResult(streamWriterCallback, contentType) + return new PushStreamHttpResult(streamWriterCallback, contentType) { LastModified = lastModified, EntityTag = entityTag, @@ -426,7 +422,7 @@ public static IResult File( { if (Path.IsPathRooted(path)) { - return new PhysicalFileResult(path, contentType) + return new PhysicalFileHttpResult(path, contentType) { FileDownloadName = fileDownloadName, LastModified = lastModified, @@ -436,7 +432,7 @@ public static IResult File( } else { - return new VirtualFileResult(path, contentType) + return new VirtualFileHttpResult(path, contentType) { FileDownloadName = fileDownloadName, LastModified = lastModified, @@ -460,7 +456,7 @@ public static IResult File( /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. /// The created for the response. public static IResult Redirect(string url, bool permanent = false, bool preserveMethod = false) - => new RedirectResult(url, permanent, preserveMethod); + => new RedirectHttpResult(url, permanent, preserveMethod); /// /// Redirects to the specified . @@ -476,7 +472,7 @@ public static IResult Redirect(string url, bool permanent = false, bool preserve /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. /// The created for the response. public static IResult LocalRedirect(string localUrl, bool permanent = false, bool preserveMethod = false) - => new LocalRedirectResult(localUrl, permanent, preserveMethod); + => new LocalRedirectHttpResult(localUrl, permanent, preserveMethod); /// /// Redirects to the specified route. @@ -494,7 +490,7 @@ public static IResult LocalRedirect(string localUrl, bool permanent = false, boo /// The fragment to add to the URL. /// The created for the response. public static IResult RedirectToRoute(string? routeName = null, object? routeValues = null, bool permanent = false, bool preserveMethod = false, string? fragment = null) - => new RedirectToRouteResult( + => new RedirectToRouteHttpResult( routeName: routeName, routeValues: routeValues, permanent: permanent, @@ -502,12 +498,12 @@ public static IResult RedirectToRoute(string? routeName = null, object? routeVal fragment: fragment); /// - /// Creates a object by specifying a . + /// Creates a object by specifying a . /// /// The status code to set on the response. - /// The created object for the response. + /// The created object for the response. public static IResult StatusCode(int statusCode) - => new StatusCodeResult(statusCode); + => new StatusCodeHttpResult(statusCode); /// /// Produces a response. @@ -515,14 +511,14 @@ public static IResult StatusCode(int statusCode) /// The value to be included in the HTTP response body. /// The created for the response. public static IResult NotFound(object? value = null) - => new NotFoundObjectResult(value); + => new NotFoundObjectHttpResult(value); /// /// Produces a response. /// /// The created for the response. public static IResult Unauthorized() - => new UnauthorizedResult(); + => new UnauthorizedHttpResult(); /// /// Produces a response. @@ -530,7 +526,7 @@ public static IResult Unauthorized() /// An error object to be included in the HTTP response body. /// The created for the response. public static IResult BadRequest(object? error = null) - => new BadRequestObjectResult(error); + => new BadRequestObjectHttpResult(error); /// /// Produces a response. @@ -538,14 +534,14 @@ public static IResult BadRequest(object? error = null) /// An error object to be included in the HTTP response body. /// The created for the response. public static IResult Conflict(object? error = null) - => new ConflictObjectResult(error); + => new ConflictObjectHttpResult(error); /// /// Produces a response. /// /// The created for the response. public static IResult NoContent() - => new NoContentResult(); + => new NoContentHttpResult(); /// /// Produces a response. @@ -553,7 +549,7 @@ public static IResult NoContent() /// The value to be included in the HTTP response body. /// The created for the response. public static IResult Ok(object? value = null) - => new OkObjectResult(value); + => new OkObjectHttpResult(value); /// /// Produces a response. @@ -561,7 +557,7 @@ public static IResult Ok(object? value = null) /// An error object to be included in the HTTP response body. /// The created for the response. public static IResult UnprocessableEntity(object? error = null) - => new UnprocessableEntityObjectResult(error); + => new UnprocessableEntityObjectHttpResult(error); /// /// Produces a response. @@ -598,10 +594,7 @@ public static IResult Problem( } } - return new ObjectResult(problemDetails) - { - ContentType = "application/problem+json", - }; + return new ProblemHttpResult(problemDetails); } /// @@ -611,10 +604,7 @@ public static IResult Problem( /// The created for the response. public static IResult Problem(ProblemDetails problemDetails) { - return new ObjectResult(problemDetails) - { - ContentType = "application/problem+json", - }; + return new ProblemHttpResult(problemDetails); } /// @@ -656,10 +646,7 @@ public static IResult ValidationProblem( } } - return new ObjectResult(problemDetails) - { - ContentType = "application/problem+json", - }; + return new ProblemHttpResult(problemDetails); } /// @@ -675,7 +662,7 @@ public static IResult Created(string uri, object? value) throw new ArgumentNullException(nameof(uri)); } - return new CreatedResult(uri, value); + return new CreatedHttpResult(uri, value); } /// @@ -691,7 +678,7 @@ public static IResult Created(Uri uri, object? value) throw new ArgumentNullException(nameof(uri)); } - return new CreatedResult(uri, value); + return new CreatedHttpResult(uri, value); } /// @@ -702,7 +689,7 @@ public static IResult Created(Uri uri, object? value) /// The value to be included in the HTTP response body. /// The created for the response. public static IResult CreatedAtRoute(string? routeName = null, object? routeValues = null, object? value = null) - => new CreatedAtRouteResult(routeName, routeValues, value); + => new CreatedAtRouteHttpResult(routeName, routeValues, value); /// /// Produces a response. @@ -711,7 +698,7 @@ public static IResult CreatedAtRoute(string? routeName = null, object? routeValu /// The optional content value to format in the response body. /// The created for the response. public static IResult Accepted(string? uri = null, object? value = null) - => new AcceptedResult(uri, value); + => new AcceptedHttpResult(uri, value); /// /// Produces a response. @@ -721,12 +708,12 @@ public static IResult Accepted(string? uri = null, object? value = null) /// The optional content value to format in the response body. /// The created for the response. public static IResult AcceptedAtRoute(string? routeName = null, object? routeValues = null, object? value = null) - => new AcceptedAtRouteResult(routeName, routeValues, value); + => new AcceptedAtRouteHttpResult(routeName, routeValues, value); /// /// Produces an empty result response, that when executed will do nothing. /// - public static IResult Empty { get; } = EmptyResult.Instance; + public static IResult Empty { get; } = EmptyHttpResult.Instance; /// /// Provides a container for external libraries to extend diff --git a/src/Http/Http.Results/src/SignInResult.cs b/src/Http/Http.Results/src/SignInHttpResult.cs similarity index 76% rename from src/Http/Http.Results/src/SignInResult.cs rename to src/Http/Http.Results/src/SignInHttpResult.cs index d0bd19996157..d5052d993142 100644 --- a/src/Http/Http.Results/src/SignInResult.cs +++ b/src/Http/Http.Results/src/SignInHttpResult.cs @@ -6,53 +6,53 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// /// An that on execution invokes . /// -internal sealed partial class SignInResult : IResult +public sealed partial class SignInHttpResult : IResult { /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// default authentication scheme. /// /// The claims principal containing the user claims. - public SignInResult(ClaimsPrincipal principal) + internal SignInHttpResult(ClaimsPrincipal principal) : this(authenticationScheme: null, principal, properties: null) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme. /// /// The authentication scheme to use when signing in the user. /// The claims principal containing the user claims. - public SignInResult(string? authenticationScheme, ClaimsPrincipal principal) + internal SignInHttpResult(string? authenticationScheme, ClaimsPrincipal principal) : this(authenticationScheme, principal, properties: null) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// default authentication scheme and . /// /// The claims principal containing the user claims. /// used to perform the sign-in operation. - public SignInResult(ClaimsPrincipal principal, AuthenticationProperties? properties) + internal SignInHttpResult(ClaimsPrincipal principal, AuthenticationProperties? properties) : this(authenticationScheme: null, principal, properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme and . /// /// The authentication schemes to use when signing in the user. /// The claims principal containing the user claims. /// used to perform the sign-in operation. - public SignInResult(string? authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties? properties) + internal SignInHttpResult(string? authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties? properties) { Principal = principal ?? throw new ArgumentNullException(nameof(principal)); AuthenticationScheme = authenticationScheme; @@ -62,22 +62,22 @@ public SignInResult(string? authenticationScheme, ClaimsPrincipal principal, Aut /// /// Gets or sets the authentication scheme that is used to perform the sign-in operation. /// - public string? AuthenticationScheme { get; set; } + public string? AuthenticationScheme { get; init; } /// /// Gets or sets the containing the user claims. /// - public ClaimsPrincipal Principal { get; set; } + public ClaimsPrincipal Principal { get; init; } /// /// Gets or sets the used to perform the sign-in operation. /// - public AuthenticationProperties? Properties { get; set; } + public AuthenticationProperties? Properties { get; init; } /// public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + var logger = httpContext.RequestServices.GetRequiredService>(); Log.SignInResultExecuting(logger, AuthenticationScheme, Principal); diff --git a/src/Http/Http.Results/src/SignOutResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs similarity index 77% rename from src/Http/Http.Results/src/SignOutResult.cs rename to src/Http/Http.Results/src/SignOutHttpResult.cs index 422037cfe9de..f7fad30e1de5 100644 --- a/src/Http/Http.Results/src/SignOutResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -6,69 +6,69 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// /// An that on execution invokes . /// -internal sealed partial class SignOutResult : IResult +public sealed partial class SignOutHttpResult : IResult { /// - /// Initializes a new instance of with the default sign out scheme. + /// Initializes a new instance of with the default sign out scheme. /// - public SignOutResult() + internal SignOutHttpResult() : this(Array.Empty()) { } /// - /// Initializes a new instance of with the default sign out scheme. + /// Initializes a new instance of with the default sign out scheme. /// specified authentication scheme and . /// /// used to perform the sign-out operation. - public SignOutResult(AuthenticationProperties properties) + internal SignOutHttpResult(AuthenticationProperties properties) : this(Array.Empty(), properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme. /// /// The authentication scheme to use when signing out the user. - public SignOutResult(string authenticationScheme) + internal SignOutHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication schemes. /// /// The authentication schemes to use when signing out the user. - public SignOutResult(IList authenticationSchemes) + internal SignOutHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication scheme and . /// /// The authentication schemes to use when signing out the user. /// used to perform the sign-out operation. - public SignOutResult(string authenticationScheme, AuthenticationProperties? properties) + internal SignOutHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } /// - /// Initializes a new instance of with the + /// Initializes a new instance of with the /// specified authentication schemes and . /// /// The authentication scheme to use when signing out the user. /// used to perform the sign-out operation. - public SignOutResult(IList authenticationSchemes, AuthenticationProperties? properties) + internal SignOutHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes ?? throw new ArgumentNullException(nameof(authenticationSchemes)); Properties = properties; @@ -87,7 +87,7 @@ public SignOutResult(IList authenticationSchemes, AuthenticationProperti /// public async Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + var logger = httpContext.RequestServices.GetRequiredService>(); Log.SignOutResultExecuting(logger, AuthenticationSchemes); diff --git a/src/Http/Http.Results/src/StatusCodeResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs similarity index 85% rename from src/Http/Http.Results/src/StatusCodeResult.cs rename to src/Http/Http.Results/src/StatusCodeHttpResult.cs index c6a2794ba67d..4a6dca002f2c 100644 --- a/src/Http/Http.Results/src/StatusCodeResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -7,7 +7,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// Represents an that when executed will +/// produce an HTTP response with the given response status code. /// public partial class StatusCodeHttpResult : IResult { @@ -17,17 +18,17 @@ internal StatusCodeHttpResult() } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// with the given . /// /// The HTTP status code of the response. - protected StatusCodeHttpResult(int statusCode) + internal StatusCodeHttpResult(int statusCode) { StatusCode = statusCode; } /// - /// Gets the HTTP status code. + /// Gets or sets the HTTP status code. /// public int StatusCode { get; init; } diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs new file mode 100644 index 000000000000..01a9e8ccda9a --- /dev/null +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +internal sealed class UnauthorizedHttpResult : StatusCodeHttpResult +{ + public UnauthorizedHttpResult() + : base(StatusCodes.Status401Unauthorized) + { + } +} diff --git a/src/Http/Http.Results/src/UnauthorizedResult.cs b/src/Http/Http.Results/src/UnauthorizedResult.cs deleted file mode 100644 index 28ec97eaf462..000000000000 --- a/src/Http/Http.Results/src/UnauthorizedResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http.Result; - -internal sealed class UnauthorizedResult : StatusCodeResult -{ - public UnauthorizedResult() : base(StatusCodes.Status401Unauthorized) - { - } -} diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs similarity index 55% rename from src/Http/Http.Results/src/UnprocessableEntityObjectResult.cs rename to src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index fd7b456eb887..0828781d3ed0 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; -internal sealed class UnprocessableEntityObjectResult : ObjectResult +internal sealed class UnprocessableEntityObjectHttpResult : ObjectHttpResult { - public UnprocessableEntityObjectResult(object? error) + public UnprocessableEntityObjectHttpResult(object? error) : base(error, StatusCodes.Status422UnprocessableEntity) { } diff --git a/src/Http/Http.Results/src/VirtualFileResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs similarity index 81% rename from src/Http/Http.Results/src/VirtualFileResult.cs rename to src/Http/Http.Results/src/VirtualFileHttpResult.cs index 38d0c32eaf2c..6be0d16dd854 100644 --- a/src/Http/Http.Results/src/VirtualFileResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -8,24 +8,24 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Microsoft.AspNetCore.Http.Result; +namespace Microsoft.AspNetCore.Http; /// -/// A that on execution writes the file specified using a virtual path to the response +/// A that on execution writes the file specified using a virtual path to the response /// using mechanisms provided by the host. /// -internal sealed class VirtualFileResult : FileResult, IResult +internal sealed class VirtualFileHttpResult : FileHttpResult, IResult { private string _fileName; private IFileInfo? _fileInfo; /// - /// Creates a new instance with the provided + /// Creates a new instance with the provided /// and the provided . /// /// The path to the file. The path must be relative/virtual. /// The Content-Type header of the response. - public VirtualFileResult(string fileName, string? contentType) + public VirtualFileHttpResult(string fileName, string? contentType) : base(contentType) { FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); @@ -41,7 +41,7 @@ public string FileName set => _fileName = value ?? throw new ArgumentNullException(nameof(value)); } - protected override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) { var response = httpContext.Response; var offset = 0L; @@ -58,9 +58,9 @@ protected override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeade count); } - protected override ILogger GetLogger(HttpContext httpContext) + protected internal override ILogger GetLogger(HttpContext httpContext) { - return httpContext.RequestServices.GetRequiredService>(); + return httpContext.RequestServices.GetRequiredService>(); } /// diff --git a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs index 5b186a474c6c..58eb0cf2f70b 100644 --- a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs @@ -27,7 +27,7 @@ public async Task ExecuteResultAsync_FormatsData() }); // Act - var result = new AcceptedAtRouteResult( + var result = new AcceptedAtRouteHttpResult( routeName: "sample", routeValues: routeValues, value: "Hello world"); @@ -69,7 +69,7 @@ public async Task ExecuteResultAsync_SetsStatusCodeAndLocationHeader(object valu var httpContext = GetHttpContext(linkGenerator); // Act - var result = new AcceptedAtRouteResult(routeValues: values, value: null); + var result = new AcceptedAtRouteHttpResult(routeValues: values, value: null); await result.ExecuteAsync(httpContext); // Assert @@ -85,7 +85,7 @@ public async Task ExecuteResultAsync_ThrowsIfRouteUrlIsNull() var httpContext = GetHttpContext(linkGenerator); // Act - var result = new AcceptedAtRouteResult( + var result = new AcceptedAtRouteHttpResult( routeName: null, routeValues: new Dictionary(), value: null); diff --git a/src/Http/Http.Results/test/AcceptedResultTests.cs b/src/Http/Http.Results/test/AcceptedResultTests.cs index 1d5f98931186..203e8ea13d1f 100644 --- a/src/Http/Http.Results/test/AcceptedResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedResultTests.cs @@ -16,7 +16,7 @@ public async Task ExecuteResultAsync_FormatsData() var stream = new MemoryStream(); httpContext.Response.Body = stream; // Act - var result = new AcceptedResult("my-location", value: "Hello world"); + var result = new AcceptedHttpResult("my-location", value: "Hello world"); await result.ExecuteAsync(httpContext); // Assert @@ -32,7 +32,7 @@ public async Task ExecuteResultAsync_SetsStatusCodeAndLocationHeader() var httpContext = GetHttpContext(); // Act - var result = new AcceptedResult(expectedUrl, value: "some-value"); + var result = new AcceptedHttpResult(expectedUrl, value: "some-value"); await result.ExecuteAsync(httpContext); // Assert diff --git a/src/Http/Http.Results/test/BadRequestObjectResultTests.cs b/src/Http/Http.Results/test/BadRequestObjectResultTests.cs index 2cddb2616671..a9868e8467d7 100644 --- a/src/Http/Http.Results/test/BadRequestObjectResultTests.cs +++ b/src/Http/Http.Results/test/BadRequestObjectResultTests.cs @@ -10,7 +10,7 @@ public void BadRequestObjectResult_SetsStatusCodeAndValue() { // Arrange & Act var obj = new object(); - var badRequestObjectResult = new BadRequestObjectResult(obj); + var badRequestObjectResult = new BadRequestObjectHttpResult(obj); // Assert Assert.Equal(StatusCodes.Status400BadRequest, badRequestObjectResult.StatusCode); diff --git a/src/Http/Http.Results/test/ChallengeResultTest.cs b/src/Http/Http.Results/test/ChallengeResultTest.cs index a5a869adb898..6cdc8dfff372 100644 --- a/src/Http/Http.Results/test/ChallengeResultTest.cs +++ b/src/Http/Http.Results/test/ChallengeResultTest.cs @@ -15,7 +15,7 @@ public class ChallengeResultTest public async Task ChallengeResult_ExecuteAsync() { // Arrange - var result = new ChallengeResult("", null); + var result = new ChallengeHttpResult("", null); var auth = new Mock(); var httpContext = GetHttpContext(auth); @@ -30,7 +30,7 @@ public async Task ChallengeResult_ExecuteAsync() public async Task ChallengeResult_ExecuteAsync_NoSchemes() { // Arrange - var result = new ChallengeResult(new string[] { }, null); + var result = new ChallengeHttpResult(new string[] { }, null); var auth = new Mock(); var httpContext = GetHttpContext(auth); diff --git a/src/Http/Http.Results/test/ConflictObjectResultTest.cs b/src/Http/Http.Results/test/ConflictObjectResultTest.cs index 4310cb3a3575..8f2de2f0f90d 100644 --- a/src/Http/Http.Results/test/ConflictObjectResultTest.cs +++ b/src/Http/Http.Results/test/ConflictObjectResultTest.cs @@ -10,7 +10,7 @@ public void ConflictObjectResult_SetsStatusCodeAndValue() { // Arrange & Act var obj = new object(); - var conflictObjectResult = new ConflictObjectResult(obj); + var conflictObjectResult = new ConflictObjectHttpResult(obj); // Assert Assert.Equal(StatusCodes.Status409Conflict, conflictObjectResult.StatusCode); diff --git a/src/Http/Http.Results/test/ContentResultTest.cs b/src/Http/Http.Results/test/ContentResultTest.cs index f373151b2b83..cb61e66e1e1c 100644 --- a/src/Http/Http.Results/test/ContentResultTest.cs +++ b/src/Http/Http.Results/test/ContentResultTest.cs @@ -15,7 +15,7 @@ public class ContentResultTest public async Task ContentResult_ExecuteAsync_Response_NullContent_SetsContentTypeAndEncoding() { // Arrange - var contentResult = new ContentResult + var contentResult = new ContentHttpResult { Content = null, ContentType = new MediaTypeHeaderValue("text/plain") @@ -109,7 +109,7 @@ public async Task ContentResult_ExecuteAsync_SetContentTypeAndEncoding_OnRespons byte[] expectedContentData) { // Arrange - var contentResult = new ContentResult + var contentResult = new ContentHttpResult { Content = content, ContentType = contentType?.ToString() @@ -133,6 +133,7 @@ private static IServiceCollection CreateServices() { var services = new ServiceCollection(); services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); + services.AddSingleton(NullLoggerFactory.Instance); return services; } diff --git a/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs b/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs index e1235d763447..7f6a45787660 100644 --- a/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs +++ b/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs @@ -39,7 +39,7 @@ public async Task CreatedAtRouteResult_ReturnsStatusCode_SetsLocationHeader(obje var httpContext = GetHttpContext(expectedUrl); // Act - var result = new CreatedAtRouteResult(routeName: null, routeValues: values, value: null); + var result = new CreatedAtRouteHttpResult(routeName: null, routeValues: values, value: null); await result.ExecuteAsync(httpContext); // Assert @@ -53,7 +53,7 @@ public async Task CreatedAtRouteResult_ThrowsOnNullUrl() // Arrange var httpContext = GetHttpContext(expectedUrl: null); - var result = new CreatedAtRouteResult( + var result = new CreatedAtRouteHttpResult( routeName: null, routeValues: new Dictionary(), value: null); diff --git a/src/Http/Http.Results/test/CreatedResultTest.cs b/src/Http/Http.Results/test/CreatedResultTest.cs index d662ba8a12d5..447c7856f65e 100644 --- a/src/Http/Http.Results/test/CreatedResultTest.cs +++ b/src/Http/Http.Results/test/CreatedResultTest.cs @@ -16,7 +16,7 @@ public void CreatedResult_SetsLocation() var location = "http://test/location"; // Act - var result = new CreatedResult(location, "testInput"); + var result = new CreatedHttpResult(location, "testInput"); // Assert Assert.Same(location, result.Location); @@ -28,7 +28,7 @@ public async Task CreatedResult_ReturnsStatusCode_SetsLocationHeader() // Arrange var location = "/test/"; var httpContext = GetHttpContext(); - var result = new CreatedResult(location, "testInput"); + var result = new CreatedHttpResult(location, "testInput"); // Act await result.ExecuteAsync(httpContext); @@ -45,7 +45,7 @@ public async Task CreatedResult_OverwritesLocationHeader() var location = "/test/"; var httpContext = GetHttpContext(); httpContext.Response.Headers["Location"] = "/different/location/"; - var result = new CreatedResult(location, "testInput"); + var result = new CreatedHttpResult(location, "testInput"); // Act await result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/EmptyResultTest.cs b/src/Http/Http.Results/test/EmptyResultTest.cs index b8771fc49985..b8dd30d4dfff 100644 --- a/src/Http/Http.Results/test/EmptyResultTest.cs +++ b/src/Http/Http.Results/test/EmptyResultTest.cs @@ -11,7 +11,7 @@ public class EmptyResultTest public async Task EmptyResult_DoesNothing() { // Arrange - var emptyResult = EmptyResult.Instance; + var emptyResult = EmptyHttpResult.Instance; // Act var httpContext = GetHttpContext(); diff --git a/src/Http/Http.Results/test/FileContentResultTest.cs b/src/Http/Http.Results/test/FileContentResultTest.cs index 2ea9ccd1c6fa..ca197faeb4e0 100644 --- a/src/Http/Http.Results/test/FileContentResultTest.cs +++ b/src/Http/Http.Results/test/FileContentResultTest.cs @@ -19,7 +19,7 @@ protected override Task ExecuteAsync( EntityTagHeaderValue entityTag = null, bool enableRangeProcessing = false) { - var result = new FileContentResult(buffer, contentType) + var result = new FileContentHttpResult(buffer, contentType) { EntityTag = entityTag, LastModified = lastModified, diff --git a/src/Http/Http.Results/test/FileStreamResultTest.cs b/src/Http/Http.Results/test/FileStreamResultTest.cs index 625b32224baf..82f7939da79a 100644 --- a/src/Http/Http.Results/test/FileStreamResultTest.cs +++ b/src/Http/Http.Results/test/FileStreamResultTest.cs @@ -16,7 +16,7 @@ protected override Task ExecuteAsync( EntityTagHeaderValue entityTag = null, bool enableRangeProcessing = false) { - var fileStreamResult = new FileStreamResult(stream, contentType) + var fileStreamResult = new FileStreamHttpResult(stream, contentType) { LastModified = lastModified, EntityTag = entityTag, @@ -33,7 +33,7 @@ public void Constructor_SetsFileName() var stream = Stream.Null; // Act - var result = new FileStreamResult(stream, "text/plain"); + var result = new FileStreamHttpResult(stream, "text/plain"); // Assert Assert.Equal(stream, result.FileStream); @@ -48,7 +48,7 @@ public void Constructor_SetsContentTypeAndParameters() var expectedMediaType = contentType; // Act - var result = new FileStreamResult(stream, contentType); + var result = new FileStreamHttpResult(stream, contentType); // Assert Assert.Equal(stream, result.FileStream); @@ -66,7 +66,7 @@ public void Constructor_SetsLastModifiedAndEtag() var entityTag = new EntityTagHeaderValue("\"Etag\""); // Act - var result = new FileStreamResult(stream, contentType) + var result = new FileStreamHttpResult(stream, contentType) { LastModified = lastModified, EntityTag = entityTag, diff --git a/src/Http/Http.Results/test/ForbidResultTest.cs b/src/Http/Http.Results/test/ForbidResultTest.cs index efab37734847..3da4f07dbadc 100644 --- a/src/Http/Http.Results/test/ForbidResultTest.cs +++ b/src/Http/Http.Results/test/ForbidResultTest.cs @@ -22,7 +22,7 @@ public async Task ExecuteResultAsync_InvokesForbidAsyncOnAuthenticationService() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new ForbidResult("", null); + var result = new ForbidHttpResult("", null); // Act await result.ExecuteAsync(httpContext); @@ -46,7 +46,7 @@ public async Task ExecuteResultAsync_InvokesForbidAsyncOnAllConfiguredSchemes() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new ForbidResult(new[] { "Scheme1", "Scheme2" }, authProperties); + var result = new ForbidHttpResult(new[] { "Scheme1", "Scheme2" }, authProperties); var routeData = new RouteData(); // Act @@ -73,7 +73,7 @@ public async Task ExecuteResultAsync_InvokesForbidAsyncWithAuthProperties(Authen .Setup(c => c.ForbidAsync(It.IsAny(), null, expected)) .Returns(Task.CompletedTask) .Verifiable(); - var result = new ForbidResult(expected); + var result = new ForbidHttpResult(expected); var httpContext = GetHttpContext(auth.Object); // Act @@ -95,7 +95,7 @@ public async Task ExecuteResultAsync_InvokesForbidAsyncWithAuthProperties_WhenAu .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new ForbidResult(expected) + var result = new ForbidHttpResult(expected) { AuthenticationSchemes = new string[0] }; diff --git a/src/Http/Http.Results/test/LocalRedirectResultTest.cs b/src/Http/Http.Results/test/LocalRedirectResultTest.cs index eff2f018bbf0..4731c93ddbc0 100644 --- a/src/Http/Http.Results/test/LocalRedirectResultTest.cs +++ b/src/Http/Http.Results/test/LocalRedirectResultTest.cs @@ -16,7 +16,7 @@ public void Constructor_WithParameterUrl_SetsResultUrlAndNotPermanentOrPreserveM var url = "/test/url"; // Act - var result = new LocalRedirectResult(url); + var result = new LocalRedirectHttpResult(url); // Assert Assert.False(result.PreserveMethod); @@ -31,7 +31,7 @@ public void Constructor_WithParameterUrlAndPermanent_SetsResultUrlAndPermanentNo var url = "/test/url"; // Act - var result = new LocalRedirectResult(url, permanent: true); + var result = new LocalRedirectHttpResult(url, permanent: true); // Assert Assert.False(result.PreserveMethod); @@ -46,7 +46,7 @@ public void Constructor_WithParameterUrlAndPermanent_SetsResultUrlPermanentAndPr var url = "/test/url"; // Act - var result = new LocalRedirectResult(url, permanent: true, preserveMethod: true); + var result = new LocalRedirectHttpResult(url, permanent: true, preserveMethod: true); // Assert Assert.True(result.PreserveMethod); @@ -63,7 +63,7 @@ public async Task Execute_ReturnsExpectedValues() var expectedPath = "/Home/About"; var httpContext = GetHttpContext(appRoot); - var result = new LocalRedirectResult(contentPath); + var result = new LocalRedirectHttpResult(contentPath); // Act await result.ExecuteAsync(httpContext); @@ -86,7 +86,7 @@ public async Task Execute_Throws_ForNonLocalUrl( { // Arrange var httpContext = GetHttpContext(appRoot); - var result = new LocalRedirectResult(contentPath); + var result = new LocalRedirectHttpResult(contentPath); // Act & Assert var exception = await Assert.ThrowsAsync(() => result.ExecuteAsync(httpContext)); @@ -107,7 +107,7 @@ public async Task Execute_Throws_ForNonLocalUrlTilde( { // Arrange var httpContext = GetHttpContext(appRoot); - var result = new LocalRedirectResult(contentPath); + var result = new LocalRedirectHttpResult(contentPath); // Act & Assert var exception = await Assert.ThrowsAsync(() => result.ExecuteAsync(httpContext)); diff --git a/src/Http/Http.Results/test/NotFoundObjectResultTest.cs b/src/Http/Http.Results/test/NotFoundObjectResultTest.cs index 1286c69d7f6e..a5361f6aa2b9 100644 --- a/src/Http/Http.Results/test/NotFoundObjectResultTest.cs +++ b/src/Http/Http.Results/test/NotFoundObjectResultTest.cs @@ -13,7 +13,7 @@ public class NotFoundObjectResultTest public void NotFoundObjectResult_InitializesStatusCode() { // Arrange & act - var notFound = new NotFoundObjectResult(null); + var notFound = new NotFoundObjectHttpResult(null); // Assert Assert.Equal(StatusCodes.Status404NotFound, notFound.StatusCode); @@ -23,7 +23,7 @@ public void NotFoundObjectResult_InitializesStatusCode() public void NotFoundObjectResult_InitializesStatusCodeAndResponseContent() { // Arrange & act - var notFound = new NotFoundObjectResult("Test Content"); + var notFound = new NotFoundObjectHttpResult("Test Content"); // Assert Assert.Equal(StatusCodes.Status404NotFound, notFound.StatusCode); @@ -35,7 +35,7 @@ public async Task NotFoundObjectResult_ExecuteSuccessful() { // Arrange var httpContext = GetHttpContext(); - var result = new NotFoundObjectResult("Test Content"); + var result = new NotFoundObjectHttpResult("Test Content"); // Act await result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/ObjectResultTests.cs b/src/Http/Http.Results/test/ObjectResultTests.cs index 3c1f24107799..9e81802a9788 100644 --- a/src/Http/Http.Results/test/ObjectResultTests.cs +++ b/src/Http/Http.Results/test/ObjectResultTests.cs @@ -16,7 +16,7 @@ public class ObjectResultTests public async Task ObjectResult_ExecuteAsync_WithNullValue_Works() { // Arrange - var result = new ObjectResult(value: null, 411); + var result = new ObjectHttpResult(value: null, 411); var httpContext = new DefaultHttpContext() { @@ -34,7 +34,7 @@ public async Task ObjectResult_ExecuteAsync_WithNullValue_Works() public async Task ObjectResult_ExecuteAsync_SetsStatusCode() { // Arrange - var result = new ObjectResult("Hello", 407); + var result = new ObjectHttpResult("Hello", 407); var httpContext = new DefaultHttpContext() { @@ -52,7 +52,7 @@ public async Task ObjectResult_ExecuteAsync_SetsStatusCode() public async Task ObjectResult_ExecuteAsync_JsonSerializesBody() { // Arrange - var result = new ObjectResult("Hello", 407); + var result = new ObjectHttpResult("Hello", 407); var stream = new MemoryStream(); var httpContext = new DefaultHttpContext() { @@ -76,7 +76,7 @@ public async Task ExecuteAsync_UsesDefaults_ForProblemDetails() // Arrange var details = new ProblemDetails(); - var result = new ObjectResult(details); + var result = new ObjectHttpResult(details); var stream = new MemoryStream(); var httpContext = new DefaultHttpContext() { @@ -105,7 +105,7 @@ public async Task ExecuteAsync_UsesDefaults_ForValidationProblemDetails() // Arrange var details = new HttpValidationProblemDetails(); - var result = new ObjectResult(details); + var result = new ObjectHttpResult(details); var stream = new MemoryStream(); var httpContext = new DefaultHttpContext() { @@ -134,7 +134,7 @@ public async Task ExecuteAsync_SetsProblemDetailsStatus_ForValidationProblemDeta // Arrange var details = new HttpValidationProblemDetails(); - var result = new ObjectResult(details, StatusCodes.Status422UnprocessableEntity); + var result = new ObjectHttpResult(details, StatusCodes.Status422UnprocessableEntity); var httpContext = new DefaultHttpContext() { RequestServices = CreateServices(), @@ -153,7 +153,7 @@ public async Task ExecuteAsync_GetsStatusCodeFromProblemDetails() // Arrange var details = new ProblemDetails { Status = StatusCodes.Status413RequestEntityTooLarge, }; - var result = new ObjectResult(details); + var result = new ObjectHttpResult(details); var httpContext = new DefaultHttpContext() { @@ -165,7 +165,7 @@ public async Task ExecuteAsync_GetsStatusCodeFromProblemDetails() // Assert Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, details.Status.Value); - Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, result.StatusCode.Value); + Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, result.StatusCode); Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, httpContext.Response.StatusCode); } @@ -175,7 +175,7 @@ public async Task ExecuteAsync_UsesStatusCodeFromResultTypeForProblemDetails() // Arrange var details = new ProblemDetails { Status = StatusCodes.Status422UnprocessableEntity, }; - var result = new BadRequestObjectResult(details); + var result = new BadRequestObjectHttpResult(details); var httpContext = new DefaultHttpContext() { @@ -187,7 +187,7 @@ public async Task ExecuteAsync_UsesStatusCodeFromResultTypeForProblemDetails() // Assert Assert.Equal(StatusCodes.Status422UnprocessableEntity, details.Status.Value); - Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode.Value); + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, httpContext.Response.StatusCode); } diff --git a/src/Http/Http.Results/test/OkObjectResultTest.cs b/src/Http/Http.Results/test/OkObjectResultTest.cs index 9a0e32b65f12..f9c8c369b723 100644 --- a/src/Http/Http.Results/test/OkObjectResultTest.cs +++ b/src/Http/Http.Results/test/OkObjectResultTest.cs @@ -13,7 +13,7 @@ public class OkObjectResultTest public async Task OkObjectResult_SetsStatusCodeAndValue() { // Arrange - var result = new OkObjectResult("Hello world"); + var result = new OkObjectHttpResult("Hello world"); var httpContext = GetHttpContext(); // Act diff --git a/src/Http/Http.Results/test/PhysicalFileResultTest.cs b/src/Http/Http.Results/test/PhysicalFileResultTest.cs index 73c5eb9144b0..6ba0c2f0b64a 100644 --- a/src/Http/Http.Results/test/PhysicalFileResultTest.cs +++ b/src/Http/Http.Results/test/PhysicalFileResultTest.cs @@ -16,7 +16,7 @@ protected override Task ExecuteAsync( EntityTagHeaderValue entityTag = null, bool enableRangeProcessing = false) { - var fileResult = new PhysicalFileResult(path, contentType) + var fileResult = new PhysicalFileHttpResult(path, contentType) { LastModified = lastModified, EntityTag = entityTag, diff --git a/src/Http/Http.Results/test/PushStreamResultTest.cs b/src/Http/Http.Results/test/PushStreamResultTest.cs index 177f1feb578e..a4791775dc75 100644 --- a/src/Http/Http.Results/test/PushStreamResultTest.cs +++ b/src/Http/Http.Results/test/PushStreamResultTest.cs @@ -12,7 +12,7 @@ public class PushStreamResultTest [Fact] public async Task PushStreamResultsExposeTheResponseBody() { - var result = new PushStreamResult(body => body.WriteAsync(Encoding.UTF8.GetBytes("Hello World").AsMemory()).AsTask(), contentType: null); + var result = new PushStreamHttpResult(body => body.WriteAsync(Encoding.UTF8.GetBytes("Hello World").AsMemory()).AsTask(), contentType: null); var httpContext = new DefaultHttpContext { @@ -37,7 +37,7 @@ public void Constructor_SetsContentTypeAndParameters() var callback = (Stream body) => body.WriteAsync(Encoding.UTF8.GetBytes("Hello World").AsMemory()).AsTask(); // Act - var result = new PushStreamResult(callback, contentType); + var result = new PushStreamHttpResult(callback, contentType); // Assert Assert.Equal(expectedMediaType, result.ContentType); @@ -55,7 +55,7 @@ public void Constructor_SetsLastModifiedAndEtag() var callback = (Stream body) => body.WriteAsync(Encoding.UTF8.GetBytes("Hello World").AsMemory()).AsTask(); // Act - var result = new PushStreamResult(callback, contentType) + var result = new PushStreamHttpResult(callback, contentType) { LastModified = lastModified, EntityTag = entityTag, diff --git a/src/Http/Http.Results/test/RedirectResultTest.cs b/src/Http/Http.Results/test/RedirectResultTest.cs index b7ba5ba764d1..207e6c6850b9 100644 --- a/src/Http/Http.Results/test/RedirectResultTest.cs +++ b/src/Http/Http.Results/test/RedirectResultTest.cs @@ -14,7 +14,7 @@ public void RedirectResult_Constructor_WithParameterUrlPermanentAndPreservesMeth var url = "/test/url"; // Act - var result = new RedirectResult(url, permanent: true, preserveMethod: true); + var result = new RedirectHttpResult(url, permanent: true, preserveMethod: true); // Assert Assert.True(result.PreserveMethod); @@ -24,7 +24,7 @@ public void RedirectResult_Constructor_WithParameterUrlPermanentAndPreservesMeth protected override Task ExecuteAsync(HttpContext httpContext, string contentPath) { - var redirectResult = new RedirectResult(contentPath, false, false); + var redirectResult = new RedirectHttpResult(contentPath, false, false); return redirectResult.ExecuteAsync(httpContext); } } diff --git a/src/Http/Http.Results/test/RedirectToRouteResultTest.cs b/src/Http/Http.Results/test/RedirectToRouteResultTest.cs index 765df0ed7af8..e92a875924a0 100644 --- a/src/Http/Http.Results/test/RedirectToRouteResultTest.cs +++ b/src/Http/Http.Results/test/RedirectToRouteResultTest.cs @@ -18,7 +18,7 @@ public async Task RedirectToRoute_Execute_ThrowsOnNullUrl() var httpContext = new DefaultHttpContext(); httpContext.RequestServices = CreateServices(null).BuildServiceProvider(); - var result = new RedirectToRouteResult(null, new Dictionary()); + var result = new RedirectToRouteHttpResult(null, new Dictionary()); // Act & Assert await ExceptionAssert.ThrowsAsync( @@ -38,7 +38,7 @@ public async Task ExecuteResultAsync_UsesRouteName_ToGenerateLocationHeader() var httpContext = GetHttpContext(locationUrl); - var result = new RedirectToRouteResult(routeName, new { id = 10 }); + var result = new RedirectToRouteHttpResult(routeName, new { id = 10 }); // Act await result.ExecuteAsync(httpContext); @@ -56,7 +56,7 @@ public async Task ExecuteResultAsync_WithFragment_PassesCorrectValuesToRedirect( var expectedStatusCode = StatusCodes.Status301MovedPermanently; var httpContext = GetHttpContext(expectedUrl); - var result = new RedirectToRouteResult("Sample", null, true, "test"); + var result = new RedirectToRouteHttpResult("Sample", null, true, "test"); // Act await result.ExecuteAsync(httpContext); @@ -74,7 +74,7 @@ public async Task ExecuteResultAsync_WithFragment_PassesCorrectValuesToRedirect_ var expectedStatusCode = StatusCodes.Status308PermanentRedirect; var httpContext = GetHttpContext(expectedUrl); - var result = new RedirectToRouteResult("Sample", null, true, true, "test"); + var result = new RedirectToRouteHttpResult("Sample", null, true, true, "test"); // Act await result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/SignInResultTest.cs b/src/Http/Http.Results/test/SignInResultTest.cs index dfcf42147c2d..f56429d4607b 100644 --- a/src/Http/Http.Results/test/SignInResultTest.cs +++ b/src/Http/Http.Results/test/SignInResultTest.cs @@ -24,7 +24,7 @@ public async Task ExecuteAsync_InvokesSignInAsyncOnAuthenticationManager() .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignInResult("", principal, null); + var result = new SignInHttpResult("", principal, null); // Act await result.ExecuteAsync(httpContext); @@ -44,7 +44,7 @@ public async Task ExecuteAsync_InvokesSignInAsyncOnAuthenticationManagerWithDefa .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignInResult(principal); + var result = new SignInHttpResult(principal); // Act await result.ExecuteAsync(httpContext); @@ -65,7 +65,7 @@ public async Task ExecuteAsync_InvokesSignInAsyncOnConfiguredScheme() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignInResult("Scheme1", principal, authProperties); + var result = new SignInHttpResult("Scheme1", principal, authProperties); // Act await result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/SignOutResultTest.cs b/src/Http/Http.Results/test/SignOutResultTest.cs index 449bc867a439..3c2682976d7a 100644 --- a/src/Http/Http.Results/test/SignOutResultTest.cs +++ b/src/Http/Http.Results/test/SignOutResultTest.cs @@ -21,7 +21,7 @@ public async Task ExecuteAsync_NoArgsInvokesDefaultSignOut() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignOutResult(); + var result = new SignOutHttpResult(); // Act await result.ExecuteAsync(httpContext); @@ -40,7 +40,7 @@ public async Task ExecuteAsync_InvokesSignOutAsyncOnAuthenticationManager() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignOutResult("", null); + var result = new SignOutHttpResult("", null); // Act await result.ExecuteAsync(httpContext); @@ -64,7 +64,7 @@ public async Task ExecuteAsync_InvokesSignOutAsyncOnAllConfiguredSchemes() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignOutResult(new[] { "Scheme1", "Scheme2" }, authProperties); + var result = new SignOutHttpResult(new[] { "Scheme1", "Scheme2" }, authProperties); // Act await result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/StatusCodeResultTests.cs b/src/Http/Http.Results/test/StatusCodeResultTests.cs index 044712ae04a4..ef6c0209fd5c 100644 --- a/src/Http/Http.Results/test/StatusCodeResultTests.cs +++ b/src/Http/Http.Results/test/StatusCodeResultTests.cs @@ -13,7 +13,7 @@ public class StatusCodeResultTests public void StatusCodeResult_ExecuteResultSetsResponseStatusCode() { // Arrange - var result = new StatusCodeResult(StatusCodes.Status404NotFound); + var result = new StatusCodeHttpResult(StatusCodes.Status404NotFound); var httpContext = GetHttpContext(); diff --git a/src/Http/Http.Results/test/UnauthorizedResultTests.cs b/src/Http/Http.Results/test/UnauthorizedResultTests.cs index e9518adf6b04..66f09a44372c 100644 --- a/src/Http/Http.Results/test/UnauthorizedResultTests.cs +++ b/src/Http/Http.Results/test/UnauthorizedResultTests.cs @@ -9,7 +9,7 @@ public class UnauthorizedResultTests public void UnauthorizedResult_InitializesStatusCode() { // Arrange & act - var result = new UnauthorizedResult(); + var result = new UnauthorizedHttpResult(); // Assert Assert.Equal(StatusCodes.Status401Unauthorized, result.StatusCode); diff --git a/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs b/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs index 6633e4f49ed9..11662d70b557 100644 --- a/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs +++ b/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs @@ -10,7 +10,7 @@ public void UnprocessableEntityObjectResult_SetsStatusCodeAndValue() { // Arrange & Act var obj = new object(); - var result = new UnprocessableEntityObjectResult(obj); + var result = new UnprocessableEntityObjectHttpResult(obj); // Assert Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); diff --git a/src/Http/Http.Results/test/VirtualFileResultTest.cs b/src/Http/Http.Results/test/VirtualFileResultTest.cs index 8d73b5f82cc7..d910676c5365 100644 --- a/src/Http/Http.Results/test/VirtualFileResultTest.cs +++ b/src/Http/Http.Results/test/VirtualFileResultTest.cs @@ -10,7 +10,7 @@ public class VirtualFileResultTest : VirtualFileResultTestBase { protected override Task ExecuteAsync(HttpContext httpContext, string path, string contentType, DateTimeOffset? lastModified = null, EntityTagHeaderValue entityTag = null, bool enableRangeProcessing = false) { - var result = new VirtualFileResult(path, contentType) + var result = new VirtualFileHttpResult(path, contentType) { LastModified = lastModified, EntityTag = entityTag, From 5d66c24331b8a36a55ed2e7a8822be2e85b5dc07 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 10 Mar 2022 22:27:50 -0800 Subject: [PATCH 04/29] Update comments --- src/Http/Http.Results/src/ContentHttpResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index 7244999c7713..e86dd0b31273 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that when executed will produce a response with content. /// public sealed partial class ContentHttpResult : StatusCodeHttpResult { From acf0a3b3f214bb679f549243a85b99fd63edddbd Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 11 Mar 2022 09:43:52 -0800 Subject: [PATCH 05/29] Make setters internal --- src/Http/Http.Results/src/ContentHttpResult.cs | 4 ++-- src/Http/Http.Results/src/FileHttpResult.cs | 10 +++++----- src/Http/Http.Results/src/ObjectHttpResult.cs | 4 ++-- src/Http/Http.Results/src/SignInHttpResult.cs | 4 ++-- src/Http/Http.Results/src/SignOutHttpResult.cs | 4 ++-- src/Http/Http.Results/src/StatusCodeHttpResult.cs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index e86dd0b31273..a8086d4f1942 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -23,12 +23,12 @@ internal ContentHttpResult() /// /// Gets or set the content representing the body of the response. /// - public string? Content { get; init; } + public string? Content { get; internal init; } /// /// Gets or sets the Content-Type header for the response. /// - public string? ContentType { get; init; } + public string? ContentType { get; internal init; } /// /// Writes the content to the HTTP response. diff --git a/src/Http/Http.Results/src/FileHttpResult.cs b/src/Http/Http.Results/src/FileHttpResult.cs index 01c10808d6ba..884c03ed8931 100644 --- a/src/Http/Http.Results/src/FileHttpResult.cs +++ b/src/Http/Http.Results/src/FileHttpResult.cs @@ -33,28 +33,28 @@ internal FileHttpResult(string? contentType) public string FileDownloadName { get { return _fileDownloadName ?? string.Empty; } - init { _fileDownloadName = value; } + internal init { _fileDownloadName = value; } } /// /// Gets or sets the last modified information associated with the . /// - public DateTimeOffset? LastModified { get; set; } + public DateTimeOffset? LastModified { get; internal set; } /// /// Gets or sets the etag associated with the . /// - public EntityTagHeaderValue? EntityTag { get; init; } + public EntityTagHeaderValue? EntityTag { get; internal init; } /// /// Gets or sets the value that enables range processing for the . /// - public bool EnableRangeProcessing { get; init; } + public bool EnableRangeProcessing { get; internal init; } /// /// Gets or sets the file length information associated with the . /// - public long? FileLength { get; set; } + public long? FileLength { get; internal set; } /// /// diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index cf4aa8128776..2596609f5a10 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -44,12 +44,12 @@ internal ObjectHttpResult(object? value, int? statusCode) /// /// Gets or sets the object result. /// - public object? Value { get; init; } + public object? Value { get; internal init; } /// /// Gets or sets the value for the Content-Type header. /// - public string? ContentType { get; init; } + public string? ContentType { get; internal init; } internal override Task WriteContentAsync(HttpContext httpContext) { diff --git a/src/Http/Http.Results/src/SignInHttpResult.cs b/src/Http/Http.Results/src/SignInHttpResult.cs index d5052d993142..3403e738ce61 100644 --- a/src/Http/Http.Results/src/SignInHttpResult.cs +++ b/src/Http/Http.Results/src/SignInHttpResult.cs @@ -62,12 +62,12 @@ internal SignInHttpResult(string? authenticationScheme, ClaimsPrincipal principa /// /// Gets or sets the authentication scheme that is used to perform the sign-in operation. /// - public string? AuthenticationScheme { get; init; } + public string? AuthenticationScheme { get; internal init; } /// /// Gets or sets the containing the user claims. /// - public ClaimsPrincipal Principal { get; init; } + public ClaimsPrincipal Principal { get; internal init; } /// /// Gets or sets the used to perform the sign-in operation. diff --git a/src/Http/Http.Results/src/SignOutHttpResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs index f7fad30e1de5..758eecc7a4c7 100644 --- a/src/Http/Http.Results/src/SignOutHttpResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -77,12 +77,12 @@ internal SignOutHttpResult(IList authenticationSchemes, AuthenticationPr /// /// Gets or sets the authentication schemes that are challenged. /// - public IList AuthenticationSchemes { get; init; } + public IList AuthenticationSchemes { get; internal init; } /// /// Gets or sets the used to perform the sign-out operation. /// - public AuthenticationProperties? Properties { get; init; } + public AuthenticationProperties? Properties { get; internal init; } /// public async Task ExecuteAsync(HttpContext httpContext) diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index 4a6dca002f2c..fc332f47321a 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -30,7 +30,7 @@ internal StatusCodeHttpResult(int statusCode) /// /// Gets or sets the HTTP status code. /// - public int StatusCode { get; init; } + public int StatusCode { get; internal init; } /// /// Sets the status code on the HTTP response. From 9ddf586d23c1d7cb0a4bedf593abd14d1392c368 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 11 Mar 2022 10:27:02 -0800 Subject: [PATCH 06/29] Updating public API file --- .../Http.Results/src/ChallengeHttpResult.cs | 4 ++-- .../Http.Results/src/PublicAPI.Unshipped.txt | 17 ----------------- src/Http/Http.Results/src/SignInHttpResult.cs | 2 +- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/Http/Http.Results/src/ChallengeHttpResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs index 2abbe533da5b..e3c73e1946c6 100644 --- a/src/Http/Http.Results/src/ChallengeHttpResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -80,12 +80,12 @@ internal ChallengeHttpResult(IList authenticationSchemes, Authentication /// /// Gets or sets the authentication schemes that are challenged. /// - public IList AuthenticationSchemes { get; init; } = Array.Empty(); + public IList AuthenticationSchemes { get; internal init; } = Array.Empty(); /// /// Gets or sets the used to perform the sign-out operation. /// - public AuthenticationProperties? Properties { get; init; } + public AuthenticationProperties? Properties { get; internal init; } /// public async Task ExecuteAsync(HttpContext httpContext) diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 92f0f6ebb0d1..f1552c688ad4 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -1,29 +1,20 @@ #nullable enable Microsoft.AspNetCore.Http.ChallengeHttpResult Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IList! -Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.init -> void Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.init -> void Microsoft.AspNetCore.Http.ContentHttpResult Microsoft.AspNetCore.Http.ContentHttpResult.Content.get -> string? -Microsoft.AspNetCore.Http.ContentHttpResult.Content.init -> void Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.get -> string? -Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.init -> void Microsoft.AspNetCore.Http.EmptyHttpResult Microsoft.AspNetCore.Http.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.FileHttpResult Microsoft.AspNetCore.Http.FileHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.FileHttpResult.EnableRangeProcessing.get -> bool -Microsoft.AspNetCore.Http.FileHttpResult.EnableRangeProcessing.init -> void Microsoft.AspNetCore.Http.FileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? -Microsoft.AspNetCore.Http.FileHttpResult.EntityTag.init -> void Microsoft.AspNetCore.Http.FileHttpResult.FileDownloadName.get -> string! -Microsoft.AspNetCore.Http.FileHttpResult.FileDownloadName.init -> void Microsoft.AspNetCore.Http.FileHttpResult.FileLength.get -> long? -Microsoft.AspNetCore.Http.FileHttpResult.FileLength.set -> void Microsoft.AspNetCore.Http.FileHttpResult.LastModified.get -> System.DateTimeOffset? -Microsoft.AspNetCore.Http.FileHttpResult.LastModified.set -> void Microsoft.AspNetCore.Http.ObjectAtLocationHttpResult Microsoft.AspNetCore.Http.ObjectAtLocationHttpResult.Location.get -> string? Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult @@ -31,9 +22,7 @@ Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? Microsoft.AspNetCore.Http.ObjectHttpResult Microsoft.AspNetCore.Http.ObjectHttpResult.ContentType.get -> string? -Microsoft.AspNetCore.Http.ObjectHttpResult.ContentType.init -> void Microsoft.AspNetCore.Http.ObjectHttpResult.Value.get -> object? -Microsoft.AspNetCore.Http.ObjectHttpResult.Value.init -> void Microsoft.AspNetCore.Http.ProblemHttpResult Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemDetails.get -> Microsoft.AspNetCore.Mvc.ProblemDetails! Microsoft.AspNetCore.Http.RedirectHttpResult @@ -51,22 +40,16 @@ Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? Microsoft.AspNetCore.Http.SignInHttpResult Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.get -> string? -Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.init -> void Microsoft.AspNetCore.Http.SignInHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.SignInHttpResult.Principal.get -> System.Security.Claims.ClaimsPrincipal! -Microsoft.AspNetCore.Http.SignInHttpResult.Principal.init -> void Microsoft.AspNetCore.Http.SignInHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.SignInHttpResult.Properties.init -> void Microsoft.AspNetCore.Http.SignOutHttpResult Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IList! -Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.init -> void Microsoft.AspNetCore.Http.SignOutHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.init -> void Microsoft.AspNetCore.Http.StatusCodeHttpResult Microsoft.AspNetCore.Http.StatusCodeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.get -> int -Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.init -> void static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, bool enableRangeProcessing = false, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! diff --git a/src/Http/Http.Results/src/SignInHttpResult.cs b/src/Http/Http.Results/src/SignInHttpResult.cs index 3403e738ce61..7b21b4b0fb46 100644 --- a/src/Http/Http.Results/src/SignInHttpResult.cs +++ b/src/Http/Http.Results/src/SignInHttpResult.cs @@ -72,7 +72,7 @@ internal SignInHttpResult(string? authenticationScheme, ClaimsPrincipal principa /// /// Gets or sets the used to perform the sign-in operation. /// - public AuthenticationProperties? Properties { get; init; } + public AuthenticationProperties? Properties { get; internal init; } /// public Task ExecuteAsync(HttpContext httpContext) From 9a79b02cdaba54de65953be236b3aa629fc1bf3b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Mar 2022 01:27:24 -0700 Subject: [PATCH 07/29] Removing base classes --- .../src/AcceptedAtRouteHttpResult.cs | 59 +++++- .../Http.Results/src/AcceptedHttpResult.cs | 60 +++++- .../src/BadRequestObjectHttpResult.cs | 23 +- .../src/ConflictObjectHttpResult.cs | 22 +- .../Http.Results/src/ContentHttpResult.cs | 14 +- .../src/CreatedAtRouteHttpResult.cs | 56 ++++- .../Http.Results/src/CreatedHttpResult.cs | 59 +++++- .../Http.Results/src/FileContentHttpResult.cs | 58 +++-- src/Http/Http.Results/src/FileHttpResult.cs | 154 -------------- .../Http.Results/src/FileStreamHttpResult.cs | 62 ++++-- .../Http.Results/src/HttpResultsWriter.cs | 200 ++++++++++++++++++ src/Http/Http.Results/src/Interfaces.cs | 59 ++++++ src/Http/Http.Results/src/JsonHttpResult.cs | 27 ++- .../src/LocalRedirectHttpResult.cs | 45 ---- .../Http.Results/src/NoContentHttpResult.cs | 21 +- .../src/NotFoundObjectHttpResult.cs | 22 +- .../src/ObjectAtLocationHttpResult.cs | 69 ------ .../src/ObjectAtRouteHttpResult.cs | 61 ------ src/Http/Http.Results/src/ObjectHttpResult.cs | 106 +--------- .../Http.Results/src/OkObjectHttpResult.cs | 24 ++- .../src/PhysicalFileHttpResult.cs | 64 ++++-- .../Http.Results/src/ProblemHttpResult.cs | 21 +- .../Http.Results/src/PublicAPI.Unshipped.txt | 67 ++---- .../Http.Results/src/PushStreamHttpResult.cs | 56 ++++- .../Http.Results/src/RedirectHttpResult.cs | 34 ++- .../src/RedirectToRouteHttpResult.cs | 25 ++- src/Http/Http.Results/src/Results.cs | 2 +- .../Http.Results/src/StatusCodeHttpResult.cs | 34 +-- .../src/UnauthorizedHttpResult.cs | 27 ++- .../UnprocessableEntityObjectHttpResult.cs | 22 +- .../Http.Results/src/VirtualFileHttpResult.cs | 89 +++++--- .../test/FileContentResultTest.cs | 1 + .../test/LocalRedirectResultTest.cs | 12 +- 33 files changed, 992 insertions(+), 663 deletions(-) delete mode 100644 src/Http/Http.Results/src/FileHttpResult.cs create mode 100644 src/Http/Http.Results/src/HttpResultsWriter.cs create mode 100644 src/Http/Http.Results/src/Interfaces.cs delete mode 100644 src/Http/Http.Results/src/LocalRedirectHttpResult.cs delete mode 100644 src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs delete mode 100644 src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index e5fcc7dee48a..0e8ecb963661 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -3,7 +3,14 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class AcceptedAtRouteHttpResult : ObjectAtRouteHttpResult +using System.Threading.Tasks; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +/// +/// +/// +public sealed class AcceptedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRouteHttpResult, IStatusCodeHttpResult { /// /// Initializes a new instance of the class with the values @@ -11,7 +18,7 @@ internal sealed class AcceptedAtRouteHttpResult : ObjectAtRouteHttpResult /// /// The route data to use for generating the URL. /// The value to format in the entity body. - public AcceptedAtRouteHttpResult(object? routeValues, object? value) + internal AcceptedAtRouteHttpResult(object? routeValues, object? value) : this(routeName: null, routeValues: routeValues, value: value) { } @@ -23,11 +30,55 @@ public AcceptedAtRouteHttpResult(object? routeValues, object? value) /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - public AcceptedAtRouteHttpResult( + internal AcceptedAtRouteHttpResult( string? routeName, object? routeValues, object? value) - : base(routeName, routeValues, value, StatusCodes.Status202Accepted) { + Value = value; + RouteName = routeName; + RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); + } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; } + + /// + /// Gets or sets the name of the route to use for generating the URL. + /// + public string? RouteName { get; } + + /// + /// Gets or sets the route data to use for generating the URL. + /// + public RouteValueDictionary? RouteValues { get; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status202Accepted; + + /// + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + + private void ConfigureResponseHeaders(HttpContext context) + { + var linkGenerator = context.RequestServices.GetRequiredService(); + var url = linkGenerator.GetUriByRouteValues( + context, + RouteName, + RouteValues, + fragment: FragmentString.Empty); + + if (string.IsNullOrEmpty(url)) + { + throw new InvalidOperationException("No route matches the supplied values."); + } + + context.Response.Headers.Location = url; } } diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index 393aa6d0caee..d950147cf2fc 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -3,14 +3,18 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class AcceptedHttpResult : ObjectAtLocationHttpResult +using System.Threading.Tasks; + +/// +/// +/// +public sealed class AcceptedHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult, IAtLocationHttpResult { /// /// Initializes a new instance of the class with the values /// provided. /// - public AcceptedHttpResult() - : base(location: null, value: null, StatusCodes.Status202Accepted) + internal AcceptedHttpResult() { } @@ -20,9 +24,10 @@ public AcceptedHttpResult() /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - public AcceptedHttpResult(string? location, object? value) - : base(location, value, StatusCodes.Status202Accepted) + internal AcceptedHttpResult(string? location, object? value) { + Value = value; + Location = location; } /// @@ -31,8 +36,49 @@ public AcceptedHttpResult(string? location, object? value) /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - public AcceptedHttpResult(Uri locationUri, object? value) - : base(locationUri, value, StatusCodes.Status202Accepted) + internal AcceptedHttpResult(Uri locationUri, object? value) + { + Value = value; + + if (locationUri == null) + { + throw new ArgumentNullException(nameof(locationUri)); + } + + if (locationUri.IsAbsoluteUri) + { + Location = locationUri.AbsoluteUri; + } + else + { + Location = locationUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; } + + /// + /// Gets or sets the HTTP status code. + /// + public int? StatusCode => StatusCodes.Status202Accepted; + + /// + /// Gets the location at which the status of the requested content can be monitored. + /// + public string? Location { get; } + + /// + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + + private void ConfigureResponseHeaders(HttpContext context) { + if (!string.IsNullOrEmpty(Location)) + { + context.Response.Headers.Location = Location; + } } } diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index ebd2e2329243..32d6231b75b6 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -3,10 +3,27 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class BadRequestObjectHttpResult : ObjectHttpResult +/// +/// +/// +public sealed class BadRequestObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { - public BadRequestObjectHttpResult(object? error) - : base(error, StatusCodes.Status400BadRequest) + internal BadRequestObjectHttpResult(object? error) { + Value = error; } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; internal init; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status400BadRequest; + + /// + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); } diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index 8250b90d9ef5..b4c185bd4a22 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -3,10 +3,26 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class ConflictObjectHttpResult : ObjectHttpResult +/// +/// +/// +public sealed class ConflictObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { - public ConflictObjectHttpResult(object? error) : - base(error, StatusCodes.Status409Conflict) + internal ConflictObjectHttpResult(object? error) { + Value = error; } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; internal init; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status409Conflict; + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); } diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index a8086d4f1942..10d79047d63d 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Http; /// /// An that when executed will produce a response with content. /// -public sealed partial class ContentHttpResult : StatusCodeHttpResult +public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult, IContentHttpResult { private const string DefaultContentType = "text/plain; charset=utf-8"; private static readonly Encoding DefaultEncoding = Encoding.UTF8; @@ -30,12 +30,17 @@ internal ContentHttpResult() /// public string? ContentType { get; internal init; } + /// + /// + /// + public int? StatusCode { get; internal init; } + /// /// Writes the content to the HTTP response. /// /// The for the current request. /// A task that represents the asynchronous execute operation. - internal override async Task WriteContentAsync(HttpContext httpContext) + public async Task ExecuteAsync(HttpContext httpContext) { var response = httpContext.Response; ResponseContentTypeHelper.ResolveContentTypeAndEncoding( @@ -48,6 +53,11 @@ internal override async Task WriteContentAsync(HttpContext httpContext) response.ContentType = resolvedContentType; + if (StatusCode != null) + { + response.StatusCode = StatusCode.Value; + } + var logger = httpContext.RequestServices.GetRequiredService>(); Log.ContentResultExecuting(logger, resolvedContentType); diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index 42d9093a6ffb..d9d4604e17d8 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -3,7 +3,13 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class CreatedAtRouteHttpResult : ObjectAtRouteHttpResult +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +/// +/// +/// +public sealed class CreatedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRouteHttpResult, IStatusCodeHttpResult { /// /// Initializes a new instance of the class with the values @@ -11,7 +17,7 @@ internal sealed class CreatedAtRouteHttpResult : ObjectAtRouteHttpResult /// /// The route data to use for generating the URL. /// The value to format in the entity body. - public CreatedAtRouteHttpResult(object? routeValues, object? value) + internal CreatedAtRouteHttpResult(object? routeValues, object? value) : this(routeName: null, routeValues: routeValues, value: value) { } @@ -23,11 +29,53 @@ public CreatedAtRouteHttpResult(object? routeValues, object? value) /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - public CreatedAtRouteHttpResult( + internal CreatedAtRouteHttpResult( string? routeName, object? routeValues, object? value) - : base(routeName, routeValues, value, StatusCodes.Status201Created) { + Value = value; + RouteName = routeName; + RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); + } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; } + + /// + /// Gets or sets the name of the route to use for generating the URL. + /// + public string? RouteName { get; } + + /// + /// Gets or sets the route data to use for generating the URL. + /// + public RouteValueDictionary? RouteValues { get; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status201Created; + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + + private void ConfigureResponseHeaders(HttpContext context) + { + var linkGenerator = context.RequestServices.GetRequiredService(); + var url = linkGenerator.GetUriByRouteValues( + context, + RouteName, + RouteValues, + fragment: FragmentString.Empty); + + if (string.IsNullOrEmpty(url)) + { + throw new InvalidOperationException("No route matches the supplied values."); + } + + context.Response.Headers.Location = url; } } diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index 5d6f6adc5d64..e8e47d4855a9 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -3,7 +3,10 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class CreatedHttpResult : ObjectAtLocationHttpResult +/// +/// +/// +public sealed class CreatedHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult, IAtLocationHttpResult { /// /// Initializes a new instance of the class with the values @@ -11,18 +14,60 @@ internal sealed class CreatedHttpResult : ObjectAtLocationHttpResult /// /// The location at which the content has been created. /// The value to format in the entity body. - public CreatedHttpResult(string location, object? value) - : base(location, value, StatusCodes.Status201Created) - { } + internal CreatedHttpResult(string location, object? value) + { + Value = value; + Location = location; + } /// /// Initializes a new instance of the class with the values /// provided. /// - /// The location at which the content has been created. + /// The location at which the content has been created. /// The value to format in the entity body. - public CreatedHttpResult(Uri location, object? value) - : base(location, value, StatusCodes.Status201Created) + internal CreatedHttpResult(Uri locationUri, object? value) + { + Value = value; + + if (locationUri == null) + { + throw new ArgumentNullException(nameof(locationUri)); + } + + if (locationUri.IsAbsoluteUri) + { + Location = locationUri.AbsoluteUri; + } + else + { + Location = locationUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; } + + /// + /// Gets or sets the HTTP status code. + /// + public int? StatusCode => StatusCodes.Status201Created; + + /// + /// Gets the location at which the status of the requested content can be monitored. + /// + public string? Location { get; } + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + + private void ConfigureResponseHeaders(HttpContext context) { + if (!string.IsNullOrEmpty(Location)) + { + context.Response.Headers.Location = Location; + } } } diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index 1f2a8a1b2ec4..ebe0fca2dfba 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -2,13 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Internal; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; -internal sealed partial class FileContentHttpResult : FileHttpResult +/// +/// +/// +public sealed partial class FileContentHttpResult : IResult, IFileHttpResult { /// /// Creates a new instance with @@ -17,25 +18,58 @@ internal sealed partial class FileContentHttpResult : FileHttpResult /// /// The bytes that represent the file contents. /// The Content-Type header of the response. - public FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) - : base(contentType) + internal FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) { FileContents = fileContents; FileLength = fileContents.Length; + ContentType = contentType ?? "application/octet-stream"; } + /// + /// Gets the Content-Type header for the response. + /// + public string ContentType { get; internal set; } + + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// + public string? FileDownloadName { get; internal set; } + + /// + /// Gets or sets the last modified information associated with the . + /// + public DateTimeOffset? LastModified { get; internal set; } + + /// + /// Gets or sets the etag associated with the . + /// + public EntityTagHeaderValue? EntityTag { get; internal init; } + + /// + /// Gets or sets the value that enables range processing for the . + /// + public bool EnableRangeProcessing { get; internal init; } + + /// + /// Gets or sets the file length information associated with the . + /// + public long? FileLength { get; internal set; } + /// /// Gets or sets the file contents. /// public ReadOnlyMemory FileContents { get; init; } - protected internal override ILogger GetLogger(HttpContext httpContext) - { - return httpContext.RequestServices.GetRequiredService>(); - } - - protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + /// + public Task ExecuteAsync(HttpContext httpContext) { - return FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength); + return HttpResultsWriter.WriteResultAsFileAsync(httpContext, + (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength), + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag); } } diff --git a/src/Http/Http.Results/src/FileHttpResult.cs b/src/Http/Http.Results/src/FileHttpResult.cs deleted file mode 100644 index 884c03ed8931..000000000000 --- a/src/Http/Http.Results/src/FileHttpResult.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Internal; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; - -namespace Microsoft.AspNetCore.Http; - -/// -/// Represents an that when executed will -/// write a file as the response. -/// -public abstract partial class FileHttpResult : IResult -{ - private string? _fileDownloadName; - - internal FileHttpResult(string? contentType) - { - ContentType = contentType ?? "application/octet-stream"; - } - - /// - /// Gets the Content-Type header for the response. - /// - public string ContentType { get; } - - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// - [AllowNull] - public string FileDownloadName - { - get { return _fileDownloadName ?? string.Empty; } - internal init { _fileDownloadName = value; } - } - - /// - /// Gets or sets the last modified information associated with the . - /// - public DateTimeOffset? LastModified { get; internal set; } - - /// - /// Gets or sets the etag associated with the . - /// - public EntityTagHeaderValue? EntityTag { get; internal init; } - - /// - /// Gets or sets the value that enables range processing for the . - /// - public bool EnableRangeProcessing { get; internal init; } - - /// - /// Gets or sets the file length information associated with the . - /// - public long? FileLength { get; internal set; } - - /// - /// - /// - /// - /// - protected internal abstract ILogger GetLogger(HttpContext httpContext); - - /// - /// - /// - /// - /// - /// - /// - protected internal abstract Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength); - - /// - /// - /// - /// - /// - public virtual Task ExecuteAsync(HttpContext httpContext) - { - var logger = GetLogger(httpContext); - - Log.ExecutingFileResult(logger, this); - - var fileResultInfo = new FileResultInfo - { - ContentType = ContentType, - EnableRangeProcessing = EnableRangeProcessing, - EntityTag = EntityTag, - FileDownloadName = FileDownloadName, - LastModified = LastModified, - }; - - var (range, rangeLength, serveBody) = FileResultHelper.SetHeadersAndLog( - httpContext, - fileResultInfo, - FileLength, - EnableRangeProcessing, - LastModified, - EntityTag, - logger); - - if (!serveBody) - { - return Task.CompletedTask; - } - - if (range != null && rangeLength == 0) - { - return Task.CompletedTask; - } - - if (range != null) - { - FileResultHelper.Log.WritingRangeToBody(logger); - } - - return ExecuteCoreAsync(httpContext, range, rangeLength); - } - - internal static partial class Log - { - public static void ExecutingFileResult(ILogger logger, FileHttpResult fileResult) - { - if (logger.IsEnabled(LogLevel.Information)) - { - var fileResultType = fileResult.GetType().Name; - ExecutingFileResultWithNoFileName(logger, fileResultType, fileResult.FileDownloadName); - } - } - - public static void ExecutingFileResult(ILogger logger, FileHttpResult fileResult, string fileName) - { - if (logger.IsEnabled(LogLevel.Information)) - { - var fileResultType = fileResult.GetType().Name; - ExecutingFileResult(logger, fileResultType, fileName, fileResult.FileDownloadName); - } - } - - [LoggerMessage(1, LogLevel.Information, - "Executing {FileResultType}, sending file with download name '{FileDownloadName}'.", - EventName = "ExecutingFileResultWithNoFileName", - SkipEnabledCheck = true)] - private static partial void ExecutingFileResultWithNoFileName(ILogger logger, string fileResultType, string fileDownloadName); - - [LoggerMessage(2, LogLevel.Information, - "Executing {FileResultType}, sending file '{FileDownloadPath}' with download name '{FileDownloadName}'.", - EventName = "ExecutingFileResult", - SkipEnabledCheck = true)] - private static partial void ExecutingFileResult(ILogger logger, string fileResultType, string fileDownloadPath, string fileDownloadName); - } -} diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index 6ba2561b1a7f..df685dbdb2ae 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -2,17 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Internal; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; /// -/// Represents an that when executed will +/// Represents an that when executed will /// write a file from a stream to the response. /// -internal sealed class FileStreamHttpResult : FileHttpResult, IResult +public sealed class FileStreamHttpResult : IResult, IFileHttpResult { /// /// Creates a new instance with @@ -21,8 +19,7 @@ internal sealed class FileStreamHttpResult : FileHttpResult, IResult /// /// The stream with the file. /// The Content-Type header of the response. - public FileStreamHttpResult(Stream fileStream, string? contentType) - : base(contentType) + internal FileStreamHttpResult(Stream fileStream, string? contentType) { if (fileStream == null) { @@ -34,28 +31,57 @@ public FileStreamHttpResult(Stream fileStream, string? contentType) { FileLength = fileStream.Length; } + ContentType = contentType ?? "application/octet-stream"; } + /// + /// Gets the Content-Type header for the response. + /// + public string ContentType { get; internal set; } + + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// + public string? FileDownloadName { get; internal set; } + + /// + /// Gets or sets the last modified information associated with the . + /// + public DateTimeOffset? LastModified { get; internal set; } + + /// + /// Gets or sets the etag associated with the . + /// + public EntityTagHeaderValue? EntityTag { get; internal init; } + + /// + /// Gets or sets the value that enables range processing for the . + /// + public bool EnableRangeProcessing { get; internal init; } + + /// + /// Gets or sets the file length information associated with the . + /// + public long? FileLength { get; internal set; } + /// /// Gets or sets the stream with the file that will be sent back as the response. /// public Stream FileStream { get; } - protected internal override ILogger GetLogger(HttpContext httpContext) - { - return httpContext.RequestServices.GetRequiredService>(); - } - - public override async Task ExecuteAsync(HttpContext httpContext) + /// + public async Task ExecuteAsync(HttpContext httpContext) { await using (FileStream) { - await base.ExecuteAsync(httpContext); + await HttpResultsWriter.WriteResultAsFileAsync(httpContext, + (context, range, rangeLength) => FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength), + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag); } } - - protected internal override Task ExecuteCoreAsync(HttpContext context, RangeItemHeaderValue? range, long rangeLength) - { - return FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength); - } } diff --git a/src/Http/Http.Results/src/HttpResultsWriter.cs b/src/Http/Http.Results/src/HttpResultsWriter.cs new file mode 100644 index 000000000000..74f2a96707de --- /dev/null +++ b/src/Http/Http.Results/src/HttpResultsWriter.cs @@ -0,0 +1,200 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +using System.Text.Json; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Internal; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; + +internal static partial class HttpResultsWriter +{ + public static void SetHttpStatusCode(HttpContext httpContext, int statusCode) + { + // TODO: Check null httpContext + + Log.StatusCodeResultExecuting(GetLogger(httpContext), statusCode); + httpContext.Response.StatusCode = statusCode; + } + + public static Task WriteResultAsJson( + HttpContext httpContext, + object? value, + string? contentType = null, + int? statusCode = null, + JsonSerializerOptions? jsonSerializerOptions = null, + Action? responseHeader = null) + { + // TODO: Check null httpContext + + Log.ObjectResultExecuting(GetLogger(httpContext), value, statusCode); + + if (value is ProblemDetails problemDetails) + { + ApplyProblemDetailsDefaults(problemDetails, statusCode); + statusCode ??= problemDetails.Status; + } + + if (statusCode is { } code) + { + SetHttpStatusCode(httpContext, code); + } + + responseHeader?.Invoke(httpContext); + + if (value is null) + { + return Task.CompletedTask; + } + + return httpContext.Response.WriteAsJsonAsync(value, value.GetType(), options: jsonSerializerOptions, contentType: contentType); + } + + public static Task WriteResultAsFileAsync( + HttpContext httpContext, + Func writeOperation, + string? fileDownloadName, + long? fileLength, + string contentType, + bool enableRangeProcessing, + DateTimeOffset? lastModified, + EntityTagHeaderValue? entityTag) + { + var logger = GetLogger(httpContext); + + Log.ExecutingFileResult(logger, fileDownloadName); + + var fileResultInfo = new FileResultInfo + { + ContentType = contentType, + EnableRangeProcessing = enableRangeProcessing, + EntityTag = entityTag, + FileDownloadName = fileDownloadName ?? string.Empty, + LastModified = lastModified, + }; + + var (range, rangeLength, serveBody) = FileResultHelper.SetHeadersAndLog( + httpContext, + fileResultInfo, + fileLength, + enableRangeProcessing, + lastModified, + entityTag, + logger); + + if (!serveBody) + { + return Task.CompletedTask; + } + + if (range != null && rangeLength == 0) + { + return Task.CompletedTask; + } + + if (range != null) + { + FileResultHelper.Log.WritingRangeToBody(logger); + } + + return writeOperation(httpContext, range, rangeLength); + } + + public static void ApplyProblemDetailsDefaults(ProblemDetails problemDetails, int? statusCode) + { + // We allow StatusCode to be specified either on ProblemDetails or on the ObjectResult and use it to configure the other. + // This lets users write return Conflict(new Problem("some description")) + // or return Problem("some-problem", 422) and have the response have consistent fields. + if (problemDetails.Status is null) + { + if (statusCode is not null) + { + problemDetails.Status = statusCode; + } + else + { + problemDetails.Status = problemDetails is HttpValidationProblemDetails ? + StatusCodes.Status400BadRequest : + StatusCodes.Status500InternalServerError; + } + } + + if (ProblemDetailsDefaults.Defaults.TryGetValue(problemDetails.Status.Value, out var defaults)) + { + problemDetails.Title ??= defaults.Title; + problemDetails.Type ??= defaults.Type; + } + } + + private static ILogger GetLogger(HttpContext httpContext) + { + var factory = httpContext.RequestServices.GetRequiredService(); + var logger = factory.CreateLogger(typeof(HttpResultsWriter)); + return logger; + } + + private static partial class Log + { + public static void ObjectResultExecuting(ILogger logger, object? value, int? statusCode) + { + if (logger.IsEnabled(LogLevel.Information)) + { + if (value is null) + { + ObjectResultExecutingWithoutValue(logger, statusCode ?? StatusCodes.Status200OK); + } + else + { + var valueType = value.GetType().FullName!; + ObjectResultExecuting(logger, valueType, statusCode ?? StatusCodes.Status200OK); + } + } + } + public static void ExecutingFileResult(ILogger logger, string? fileDownloadName) + { + if (logger.IsEnabled(LogLevel.Information)) + { + //TODO fix + var fileResultType = "";// fileResult.GetType().Name; + ExecutingFileResultWithNoFileName(logger, fileResultType, fileDownloadName: fileDownloadName ?? string.Empty); + } + } + + public static void ExecutingFileResult(ILogger logger, string? fileDownloadName, string fileName) + { + if (logger.IsEnabled(LogLevel.Information)) + { + //TODO fix + var fileResultType = "";//fileResult.GetType().Name; + ExecutingFileResult(logger, fileResultType, fileName, fileDownloadName: fileDownloadName ?? string.Empty); + } + } + + [LoggerMessage(1, LogLevel.Information, + "Executing StatusCodeResult, setting HTTP status code {StatusCode}.", + EventName = "StatusCodeResultExecuting")] + public static partial void StatusCodeResultExecuting(ILogger logger, int statusCode); + + [LoggerMessage(2, LogLevel.Information, "Writing value of type '{Type}' with status code '{StatusCode}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)] + private static partial void ObjectResultExecuting(ILogger logger, string type, int statusCode); + + [LoggerMessage(3, LogLevel.Information, "Executing result with status code '{StatusCode}'.", EventName = "ObjectResultExecutingWithoutValue", SkipEnabledCheck = true)] + private static partial void ObjectResultExecutingWithoutValue(ILogger logger, int statusCode); + + [LoggerMessage(4, LogLevel.Information, + "Executing {FileResultType}, sending file with download name '{FileDownloadName}'.", + EventName = "ExecutingFileResultWithNoFileName", + SkipEnabledCheck = true)] + private static partial void ExecutingFileResultWithNoFileName(ILogger logger, string fileResultType, string fileDownloadName); + + [LoggerMessage(5, LogLevel.Information, + "Executing {FileResultType}, sending file '{FileDownloadPath}' with download name '{FileDownloadName}'.", + EventName = "ExecutingFileResult", + SkipEnabledCheck = true)] + private static partial void ExecutingFileResult(ILogger logger, string fileResultType, string fileDownloadPath, string fileDownloadName); + } +} diff --git a/src/Http/Http.Results/src/Interfaces.cs b/src/Http/Http.Results/src/Interfaces.cs new file mode 100644 index 000000000000..29f4c6f4117c --- /dev/null +++ b/src/Http/Http.Results/src/Interfaces.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using Microsoft.Net.Http.Headers; + +public interface IStatusCodeHttpResult +{ + public int? StatusCode { get; } +} + +public interface IObjectHttpResult +{ + public object? Value { get; } +} + +public interface IProblemHttpResult +{ + public ProblemDetails ProblemDetails { get; } + public string ContentType { get; } +} + +public interface IAtRouteHttpResult +{ + public string? RouteName { get; } + public RouteValueDictionary? RouteValues { get; } +} + +public interface IAtLocationHttpResult +{ + public string? Location { get; } +} + +public interface IContentHttpResult +{ + public string? Content { get; } + public string? ContentType { get; } +} + +public interface IRedirectHttpResult +{ + public bool AcceptLocalUrlOnly { get; } + public bool Permanent { get; } + public bool PreserveMethod { get; } + public string? Url { get; } +} + +public interface IFileHttpResult +{ + public string ContentType { get; } + public string? FileDownloadName { get; } + public DateTimeOffset? LastModified { get; } + public EntityTagHeaderValue? EntityTag { get; } + public bool EnableRangeProcessing { get; } + public long? FileLength { get; } +} diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index d7a8e7b38234..11d04c238fd6 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -8,17 +8,17 @@ namespace Microsoft.AspNetCore.Http; /// /// An action result which formats the given object as JSON. /// -internal sealed partial class JsonHttpResult : ObjectHttpResult +public sealed class JsonHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) - : base(value) + : this(value, statusCode: null, jsonSerializerOptions: jsonSerializerOptions) { - JsonSerializerOptions = jsonSerializerOptions; } public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) - : base(value, statusCode) { + Value = value; + StatusCode = statusCode; JsonSerializerOptions = jsonSerializerOptions; } @@ -33,6 +33,21 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso /// public JsonSerializerOptions? JsonSerializerOptions { get; init; } - protected internal override Task WriteHttpResultAsync(HttpContext httpContext) - => httpContext.Response.WriteAsJsonAsync(Value, JsonSerializerOptions, ContentType); + /// + /// Gets or sets the object result. + /// + public object? Value { get; } + + /// + /// Gets or sets the value for the Content-Type header. + /// + public string? ContentType { get; internal set; } + + /// + /// + /// + public int? StatusCode { get; } + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, ContentType, StatusCode, JsonSerializerOptions); } diff --git a/src/Http/Http.Results/src/LocalRedirectHttpResult.cs b/src/Http/Http.Results/src/LocalRedirectHttpResult.cs deleted file mode 100644 index 1f4b2537e777..000000000000 --- a/src/Http/Http.Results/src/LocalRedirectHttpResult.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), -/// or Permanent Redirect (308) response with a Location header to the supplied local URL. -/// -internal sealed partial class LocalRedirectHttpResult : RedirectHttpResult -{ - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The local URL to redirect to. - public LocalRedirectHttpResult(string localUrl) - : this(localUrl, permanent: false) - { - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The local URL to redirect to. - /// Specifies whether the redirect should be permanent (301) or temporary (302). - public LocalRedirectHttpResult(string localUrl, bool permanent) - : this(localUrl, permanent, preserveMethod: false) - { - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The local URL to redirect to. - /// Specifies whether the redirect should be permanent (301) or temporary (302). - /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request's method. - public LocalRedirectHttpResult(string localUrl, bool permanent, bool preserveMethod) - : base(localUrl, permanent, preserveMethod, acceptLocalUrlOnly: true) - { - - } -} diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index 7f98c4c3b304..bcbc491248ab 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -3,10 +3,25 @@ namespace Microsoft.AspNetCore.Http; -internal class NoContentHttpResult : StatusCodeHttpResult +/// +/// +/// +public class NoContentHttpResult : IResult, IStatusCodeHttpResult { - public NoContentHttpResult() - : base(StatusCodes.Status204NoContent) + private readonly int _statuCode = StatusCodes.Status204NoContent; + + internal NoContentHttpResult() + { + } + + /// + /// + /// + public int? StatusCode => _statuCode; + + public Task ExecuteAsync(HttpContext httpContext) { + HttpResultsWriter.SetHttpStatusCode(httpContext, _statuCode); + return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index cb02f7f9645e..4d716baf2b38 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -3,10 +3,26 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class NotFoundObjectHttpResult : ObjectHttpResult +/// +/// +/// +public sealed class NotFoundObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { - public NotFoundObjectHttpResult(object? value) - : base(value, StatusCodes.Status404NotFound) + internal NotFoundObjectHttpResult(object? value) { + Value = value; } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; internal init; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status404NotFound; + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); } diff --git a/src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs b/src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs deleted file mode 100644 index 9fb491934111..000000000000 --- a/src/Http/Http.Results/src/ObjectAtLocationHttpResult.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// An that on execution will write an object to the response -/// with a Location header to the supplied URL. -/// -public abstract class ObjectAtLocationHttpResult : ObjectHttpResult -{ - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The location at which the status of requested content can be monitored. - /// The value to format in the entity body. - /// The HTTP status code of the response. - internal ObjectAtLocationHttpResult( - string? location, - object? value, - int? statusCode) - : base(value, statusCode) - { - Location = location; - } - - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The location at which the status of requested content can be monitored. - /// The value to format in the entity body. - /// The HTTP status code of the response. - internal ObjectAtLocationHttpResult( - Uri locationUri, - object? value, - int? statusCode) - : base(value, statusCode) - { - if (locationUri == null) - { - throw new ArgumentNullException(nameof(locationUri)); - } - - if (locationUri.IsAbsoluteUri) - { - Location = locationUri.AbsoluteUri; - } - else - { - Location = locationUri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); - } - } - - /// - /// Gets the location at which the status of the requested content can be monitored. - /// - public string? Location { get; } - - /// - protected internal override void ConfigureResponseHeaders(HttpContext context) - { - if (!string.IsNullOrEmpty(Location)) - { - context.Response.Headers.Location = Location; - } - } -} diff --git a/src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs b/src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs deleted file mode 100644 index add99c920a56..000000000000 --- a/src/Http/Http.Results/src/ObjectAtRouteHttpResult.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Http; - -/// -/// An that on execution will write an object to the response -/// with a Location header. Targets a registered route. -/// -public abstract class ObjectAtRouteHttpResult : ObjectHttpResult -{ - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - /// The name of the route to use for generating the URL. - /// The route data to use for generating the URL. - /// The value to format in the entity body. - /// The HTTP status code of the response. - internal ObjectAtRouteHttpResult( - string? routeName, - object? routeValues, - object? value, - int? statusCode) - : base(value, statusCode) - { - RouteName = routeName; - RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); - } - - /// - /// Gets or sets the name of the route to use for generating the URL. - /// - public string? RouteName { get; } - - /// - /// Gets or sets the route data to use for generating the URL. - /// - public RouteValueDictionary? RouteValues { get; } - - /// - protected internal override void ConfigureResponseHeaders(HttpContext context) - { - var linkGenerator = context.RequestServices.GetRequiredService(); - var url = linkGenerator.GetUriByRouteValues( - context, - RouteName, - RouteValues, - fragment: FragmentString.Empty); - - if (string.IsNullOrEmpty(url)) - { - throw new InvalidOperationException("No route matches the supplied values."); - } - - context.Response.Headers.Location = url; - } -} diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index 2596609f5a10..1f8d620cbabb 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -1,17 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - namespace Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + /// /// An that on execution will write an object to the response. /// -public partial class ObjectHttpResult : StatusCodeHttpResult +internal sealed class ObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { /// /// Creates a new instance @@ -30,15 +27,13 @@ internal ObjectHttpResult(object? value, int? statusCode) { Value = value; - if (Value is ProblemDetails problemDetails) + if (value is ProblemDetails problemDetails) { - statusCode = ApplyProblemDetailsDefaults(problemDetails, statusCode); + HttpResultsWriter.ApplyProblemDetailsDefaults(problemDetails, statusCode); + statusCode ??= problemDetails.Status; } - if (statusCode is { } status) - { - StatusCode = status; - } + StatusCode = statusCode; } /// @@ -51,97 +46,16 @@ internal ObjectHttpResult(object? value, int? statusCode) /// public string? ContentType { get; internal init; } - internal override Task WriteContentAsync(HttpContext httpContext) - { - var loggerFactory = httpContext.RequestServices.GetRequiredService(); - var logger = loggerFactory.CreateLogger(GetType()); - Log.ObjectResultExecuting(logger, Value, StatusCode); - - ConfigureResponseHeaders(httpContext); - - if (Value is null) - { - return Task.CompletedTask; - } - - OnFormatting(httpContext); - return WriteHttpResultAsync(httpContext); - } - - /// - /// - /// - /// - protected internal virtual void OnFormatting(HttpContext httpContext) - { - } - /// /// /// - /// - protected internal virtual void ConfigureResponseHeaders(HttpContext httpContext) - { - } + public int? StatusCode { get; internal init; } /// /// /// /// /// - protected internal virtual Task WriteHttpResultAsync(HttpContext httpContext) - => httpContext.Response.WriteAsJsonAsync(Value, Value!.GetType(), options: null, contentType: ContentType); - - private static int? ApplyProblemDetailsDefaults(ProblemDetails problemDetails, int? statusCode) - { - // We allow StatusCode to be specified either on ProblemDetails or on the ObjectResult and use it to configure the other. - // This lets users write return Conflict(new Problem("some description")) - // or return Problem("some-problem", 422) and have the response have consistent fields. - if (problemDetails.Status is null) - { - if (statusCode is not null) - { - problemDetails.Status = statusCode; - } - else - { - problemDetails.Status = problemDetails is HttpValidationProblemDetails ? - StatusCodes.Status400BadRequest : - StatusCodes.Status500InternalServerError; - } - } - - if (ProblemDetailsDefaults.Defaults.TryGetValue(problemDetails.Status.Value, out var defaults)) - { - problemDetails.Title ??= defaults.Title; - problemDetails.Type ??= defaults.Type; - } - - return statusCode ?? problemDetails.Status; - } - - private static partial class Log - { - public static void ObjectResultExecuting(ILogger logger, object? value, int? statusCode) - { - if (logger.IsEnabled(LogLevel.Information)) - { - if (value is null) - { - ObjectResultExecutingWithoutValue(logger, statusCode ?? StatusCodes.Status200OK); - } - else - { - var valueType = value.GetType().FullName!; - ObjectResultExecuting(logger, valueType, statusCode ?? StatusCodes.Status200OK); - } - } - } - - [LoggerMessage(1, LogLevel.Information, "Writing value of type '{Type}' with status code '{StatusCode}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)] - private static partial void ObjectResultExecuting(ILogger logger, string type, int statusCode); - - [LoggerMessage(2, LogLevel.Information, "Executing result with status code '{StatusCode}'.", EventName = "ObjectResultExecutingWithoutValue", SkipEnabledCheck = true)] - private static partial void ObjectResultExecutingWithoutValue(ILogger logger, int statusCode); - } + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, ContentType, StatusCode); } diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index ea97d7239d07..cdc1ac0d11ee 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -3,10 +3,28 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class OkObjectHttpResult : ObjectHttpResult +using System.Threading.Tasks; + +/// +/// +/// +public sealed class OkObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { - public OkObjectHttpResult(object? value) - : base(value, StatusCodes.Status200OK) + internal OkObjectHttpResult(object? value) { + Value = value; } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; internal init; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status200OK; + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); } diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index afbd653179f3..71bc0bc48850 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -11,7 +9,7 @@ namespace Microsoft.AspNetCore.Http; /// A on execution will write a file from disk to the response /// using mechanisms provided by the host. /// -internal sealed partial class PhysicalFileHttpResult : FileHttpResult, IResult +public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult { /// /// Creates a new instance with @@ -19,27 +17,58 @@ internal sealed partial class PhysicalFileHttpResult : FileHttpResult, IResult /// /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. - public PhysicalFileHttpResult(string fileName, string? contentType) - : base(contentType) + internal PhysicalFileHttpResult(string fileName, string? contentType) { FileName = fileName; + ContentType = contentType ?? "application/octet-stream"; } + /// + /// Gets the Content-Type header for the response. + /// + public string ContentType { get; internal set;} + + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// + public string? FileDownloadName { get; internal set;} + + /// + /// Gets or sets the last modified information associated with the . + /// + public DateTimeOffset? LastModified { get; internal set; } + + /// + /// Gets or sets the etag associated with the . + /// + public EntityTagHeaderValue? EntityTag { get; internal init; } + + /// + /// Gets or sets the value that enables range processing for the . + /// + public bool EnableRangeProcessing { get; internal init; } + + /// + /// Gets or sets the file length information associated with the . + /// + public long? FileLength { get; internal set; } + /// /// Gets or sets the path to the file that will be sent back as the response. /// public string FileName { get; } // For testing - public Func GetFileInfoWrapper { get; init; } = + internal Func GetFileInfoWrapper { get; init; } = static path => new FileInfoWrapper(path); - protected internal override ILogger GetLogger(HttpContext httpContext) - { - return httpContext.RequestServices.GetRequiredService>(); - } - - public override Task ExecuteAsync(HttpContext httpContext) + /// + /// + /// + /// + /// + /// + public Task ExecuteAsync(HttpContext httpContext) { var fileInfo = GetFileInfoWrapper(FileName); if (!fileInfo.Exists) @@ -50,10 +79,17 @@ public override Task ExecuteAsync(HttpContext httpContext) LastModified = LastModified ?? fileInfo.LastWriteTimeUtc; FileLength = fileInfo.Length; - return base.ExecuteAsync(httpContext); + return HttpResultsWriter.WriteResultAsFileAsync(httpContext, + ExecuteCoreAsync, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag); } - protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) { var response = httpContext.Response; if (!Path.IsPathRooted(FileName)) diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 9e50d2720a00..ba5ec3acf6b9 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -9,17 +9,24 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write Problem Details /// HTTP API responses based on https://tools.ietf.org/html/rfc7807 /// -public sealed class ProblemHttpResult : ObjectHttpResult +public sealed class ProblemHttpResult : IResult, IProblemHttpResult { + internal ProblemHttpResult(ProblemDetails problemDetails) + { + ProblemDetails = problemDetails; + } + /// /// Gets the instance. /// public ProblemDetails ProblemDetails { get; } - internal ProblemHttpResult(ProblemDetails problemDetails) - : base(problemDetails) - { - ContentType = "application/problem+json"; - ProblemDetails = problemDetails; - } + /// + /// Gets or sets the value for the Content-Type header. + /// + public string ContentType => "application/problem+json"; + + /// + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, ProblemDetails, ContentType); } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index f1552c688ad4..6506d67f2e3a 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -1,57 +1,24 @@ #nullable enable -Microsoft.AspNetCore.Http.ChallengeHttpResult -Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IList! -Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.ContentHttpResult -Microsoft.AspNetCore.Http.ContentHttpResult.Content.get -> string? -Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.get -> string? -Microsoft.AspNetCore.Http.EmptyHttpResult -Microsoft.AspNetCore.Http.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.FileHttpResult -Microsoft.AspNetCore.Http.FileHttpResult.ContentType.get -> string! -Microsoft.AspNetCore.Http.FileHttpResult.EnableRangeProcessing.get -> bool -Microsoft.AspNetCore.Http.FileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? -Microsoft.AspNetCore.Http.FileHttpResult.FileDownloadName.get -> string! -Microsoft.AspNetCore.Http.FileHttpResult.FileLength.get -> long? -Microsoft.AspNetCore.Http.FileHttpResult.LastModified.get -> System.DateTimeOffset? -Microsoft.AspNetCore.Http.ObjectAtLocationHttpResult -Microsoft.AspNetCore.Http.ObjectAtLocationHttpResult.Location.get -> string? -Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult -Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult.RouteName.get -> string? -Microsoft.AspNetCore.Http.ObjectAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? -Microsoft.AspNetCore.Http.ObjectHttpResult -Microsoft.AspNetCore.Http.ObjectHttpResult.ContentType.get -> string? -Microsoft.AspNetCore.Http.ObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteName.get -> string? +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.AcceptedHttpResult +Microsoft.AspNetCore.Http.AcceptedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.AcceptedHttpResult.Location.get -> string? +Microsoft.AspNetCore.Http.AcceptedHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.AcceptedHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.BadRequestObjectHttpResult +Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.ProblemHttpResult +Microsoft.AspNetCore.Http.ProblemHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.ProblemHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemDetails.get -> Microsoft.AspNetCore.Mvc.ProblemDetails! -Microsoft.AspNetCore.Http.RedirectHttpResult -Microsoft.AspNetCore.Http.RedirectHttpResult.AcceptLocalUrlOnly.get -> bool -Microsoft.AspNetCore.Http.RedirectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.RedirectHttpResult.Permanent.get -> bool -Microsoft.AspNetCore.Http.RedirectHttpResult.PreserveMethod.get -> bool -Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string! -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Fragment.get -> string? -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Permanent.get -> bool -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.PreserveMethod.get -> bool -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteName.get -> string? -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? -Microsoft.AspNetCore.Http.SignInHttpResult -Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.get -> string? -Microsoft.AspNetCore.Http.SignInHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.SignInHttpResult.Principal.get -> System.Security.Claims.ClaimsPrincipal! -Microsoft.AspNetCore.Http.SignInHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.SignOutHttpResult -Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IList! -Microsoft.AspNetCore.Http.SignOutHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.StatusCodeHttpResult -Microsoft.AspNetCore.Http.StatusCodeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.get -> int static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, bool enableRangeProcessing = false, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.IO.Pipelines.PipeReader! pipeReader, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) -> Microsoft.AspNetCore.Http.IResult! -virtual Microsoft.AspNetCore.Http.FileHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index 2c310a9f5d79..fd55896c63d5 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -1,29 +1,63 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; -internal sealed class PushStreamHttpResult : FileHttpResult +/// +/// +/// +public sealed class PushStreamHttpResult : IResult, IFileHttpResult { private readonly Func _streamWriterCallback; - public PushStreamHttpResult(Func streamWriterCallback, string? contentType) - : base(contentType) + internal PushStreamHttpResult(Func streamWriterCallback, string? contentType) { _streamWriterCallback = streamWriterCallback; + ContentType = contentType ?? "application/octet-stream"; } - protected internal override ILogger GetLogger(HttpContext httpContext) - { - return httpContext.RequestServices.GetRequiredService>(); - } + /// + /// Gets the Content-Type header for the response. + /// + public string ContentType { get; internal set; } + + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// + public string? FileDownloadName { get; internal set; } + + /// + /// Gets or sets the last modified information associated with the . + /// + public DateTimeOffset? LastModified { get; internal set; } + + /// + /// Gets or sets the etag associated with the . + /// + public EntityTagHeaderValue? EntityTag { get; internal init; } + + /// + /// Gets or sets the value that enables range processing for the . + /// + public bool EnableRangeProcessing { get; internal init; } + + /// + /// Gets or sets the file length information associated with the . + /// + public long? FileLength { get; internal set; } - protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + /// + public Task ExecuteAsync(HttpContext httpContext) { - return _streamWriterCallback(httpContext.Response.Body); + return HttpResultsWriter.WriteResultAsFileAsync(httpContext, + (context, _, _) => _streamWriterCallback(httpContext.Response.Body), + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag); } } diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index 16fd3565535c..47a47169b8be 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -11,8 +11,33 @@ namespace Microsoft.AspNetCore.Http; /// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), /// or Permanent Redirect (308) response with a Location header to the supplied URL. /// -public partial class RedirectHttpResult : IResult +public sealed partial class RedirectHttpResult : IResult, IRedirectHttpResult { + private readonly string _url; + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The URL to redirect to. + /// If set to true, only local URLs are accepted and will throw an exception when the supplied URL is not considered local. (Default: false) + internal RedirectHttpResult(string url, bool acceptLocalUrlOnly = false) + : this(url, permanent: false, acceptLocalUrlOnly) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The URL to redirect to. + /// Specifies whether the redirect should be permanent (301) or temporary (302). + /// If set to true, only local URLs are accepted and will throw an exception when the supplied URL is not considered local. (Default: false) + internal RedirectHttpResult(string url, bool permanent, bool acceptLocalUrlOnly = false) + : this(url, permanent, preserveMethod: false, acceptLocalUrlOnly) + { + } + /// /// Initializes a new instance of the class with the values /// provided. @@ -36,7 +61,8 @@ internal RedirectHttpResult(string url, bool permanent, bool preserveMethod, boo Permanent = permanent; PreserveMethod = preserveMethod; AcceptLocalUrlOnly = acceptLocalUrlOnly; - Url = url; + + _url = url; } /// @@ -52,7 +78,7 @@ internal RedirectHttpResult(string url, bool permanent, bool preserveMethod, boo /// /// Gets the URL to redirect to. /// - public string Url { get; } + public string? Url => _url; /// /// Gets an indication that only local URLs are accepted. @@ -71,7 +97,7 @@ public Task ExecuteAsync(HttpContext httpContext) } // IsLocalUrl is called to handle URLs starting with '~/'. - var destinationUrl = isLocalUrl ? SharedUrlHelper.Content(httpContext, Url) : Url; + var destinationUrl = isLocalUrl ? SharedUrlHelper.Content(httpContext, contentPath: _url) : _url; Log.RedirectResultExecuting(logger, destinationUrl); diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index b00808b983a2..3eb71a319874 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -12,8 +12,10 @@ namespace Microsoft.AspNetCore.Http; /// or Permanent Redirect (308) response with a Location header. /// Targets a registered route. /// -public sealed partial class RedirectToRouteHttpResult : IResult +public sealed partial class RedirectToRouteHttpResult : IResult, IRedirectHttpResult, IAtRouteHttpResult { + private string? _destinationUrl; + /// /// Initializes a new instance of the with the values /// provided. @@ -149,33 +151,44 @@ internal RedirectToRouteHttpResult( /// public string? Fragment { get; } + /// + /// Gets an indication that only local URLs are accepted. + /// + public bool AcceptLocalUrlOnly => true; + + /// + /// Gets the URL to redirect to. + /// + public string? Url => _destinationUrl; + /// public Task ExecuteAsync(HttpContext httpContext) { var linkGenerator = httpContext.RequestServices.GetRequiredService(); - var destinationUrl = linkGenerator.GetUriByRouteValues( + _destinationUrl = linkGenerator.GetUriByRouteValues( httpContext, RouteName, RouteValues, fragment: Fragment == null ? FragmentString.Empty : new FragmentString("#" + Fragment)); - if (string.IsNullOrEmpty(destinationUrl)) + + if (string.IsNullOrEmpty(_destinationUrl)) { throw new InvalidOperationException("No route matches the supplied values."); } var logger = httpContext.RequestServices.GetRequiredService>(); - Log.RedirectToRouteResultExecuting(logger, destinationUrl, RouteName); + Log.RedirectToRouteResultExecuting(logger, _destinationUrl, RouteName); if (PreserveMethod) { httpContext.Response.StatusCode = Permanent ? StatusCodes.Status308PermanentRedirect : StatusCodes.Status307TemporaryRedirect; - httpContext.Response.Headers.Location = destinationUrl; + httpContext.Response.Headers.Location = _destinationUrl; } else { - httpContext.Response.Redirect(destinationUrl, Permanent); + httpContext.Response.Redirect(_destinationUrl, Permanent); } return Task.CompletedTask; diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index e5158c8e8eb8..3d363aa6fe16 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -472,7 +472,7 @@ public static IResult Redirect(string url, bool permanent = false, bool preserve /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. /// The created for the response. public static IResult LocalRedirect(string localUrl, bool permanent = false, bool preserveMethod = false) - => new LocalRedirectHttpResult(localUrl, permanent, preserveMethod); + => new RedirectHttpResult(localUrl, permanent, preserveMethod, acceptLocalUrlOnly: true); /// /// Redirects to the specified route. diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index fc332f47321a..86c7311dee03 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -1,21 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - namespace Microsoft.AspNetCore.Http; /// /// Represents an that when executed will /// produce an HTTP response with the given response status code. /// -public partial class StatusCodeHttpResult : IResult +public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult { - internal StatusCodeHttpResult() - : this(StatusCodes.Status200OK) - { - } + private readonly int _statuCode; /// /// Initializes a new instance of the class @@ -24,13 +18,13 @@ internal StatusCodeHttpResult() /// The HTTP status code of the response. internal StatusCodeHttpResult(int statusCode) { - StatusCode = statusCode; + _statuCode = statusCode; } /// /// Gets or sets the HTTP status code. /// - public int StatusCode { get; internal init; } + public int? StatusCode => _statuCode; /// /// Sets the status code on the HTTP response. @@ -39,25 +33,7 @@ internal StatusCodeHttpResult(int statusCode) /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) { - var factory = httpContext.RequestServices.GetRequiredService(); - var logger = factory.CreateLogger(GetType()); - - Log.StatusCodeResultExecuting(logger, StatusCode); - httpContext.Response.StatusCode = StatusCode; - - return WriteContentAsync(httpContext); - } - - internal virtual Task WriteContentAsync(HttpContext httpContext) - { + HttpResultsWriter.SetHttpStatusCode(httpContext, _statuCode); return Task.CompletedTask; } - - private static partial class Log - { - [LoggerMessage(1, LogLevel.Information, - "Executing StatusCodeResult, setting HTTP status code {StatusCode}.", - EventName = "StatusCodeResultExecuting")] - public static partial void StatusCodeResultExecuting(ILogger logger, int statusCode); - } } diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index 01a9e8ccda9a..c61c387a5399 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -3,10 +3,31 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class UnauthorizedHttpResult : StatusCodeHttpResult +/// +/// +/// +public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult { - public UnauthorizedHttpResult() - : base(StatusCodes.Status401Unauthorized) + private readonly int _statuCode = StatusCodes.Status401Unauthorized; + + internal UnauthorizedHttpResult() + { + } + + /// + /// Gets or sets the HTTP status code. + /// + public int? StatusCode => _statuCode; + + /// + /// Sets the status code on the HTTP response. + /// + /// The for the current request. + /// A task that represents the asynchronous execute operation. + public Task ExecuteAsync(HttpContext httpContext) { + HttpResultsWriter.SetHttpStatusCode(httpContext, _statuCode); + return Task.CompletedTask; } + } diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index 0828781d3ed0..bbf1283f5c9f 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -3,10 +3,26 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class UnprocessableEntityObjectHttpResult : ObjectHttpResult +/// +/// +/// +public sealed class UnprocessableEntityObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { - public UnprocessableEntityObjectHttpResult(object? error) - : base(error, StatusCodes.Status422UnprocessableEntity) + internal UnprocessableEntityObjectHttpResult(object? error) { + Value = error; } + + /// + /// Gets or sets the object result. + /// + public object? Value { get; internal init; } + + /// + /// + /// + public int? StatusCode => StatusCodes.Status422UnprocessableEntity; + + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); } diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index 6be0d16dd854..fee68eba7efb 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -5,16 +5,15 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; /// -/// A that on execution writes the file specified using a virtual path to the response +/// A that on execution writes the file specified using a virtual path to the response /// using mechanisms provided by the host. /// -internal sealed class VirtualFileHttpResult : FileHttpResult, IResult +public sealed class VirtualFileHttpResult : IResult, IFileHttpResult { private string _fileName; private IFileInfo? _fileInfo; @@ -25,12 +24,42 @@ internal sealed class VirtualFileHttpResult : FileHttpResult, IResult /// /// The path to the file. The path must be relative/virtual. /// The Content-Type header of the response. - public VirtualFileHttpResult(string fileName, string? contentType) - : base(contentType) + internal VirtualFileHttpResult(string fileName, string? contentType) { FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); + ContentType = contentType ?? "application/octet-stream"; } + /// + /// Gets the Content-Type header for the response. + /// + public string ContentType { get; internal set; } + + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// + public string? FileDownloadName { get; internal set; } + + /// + /// Gets or sets the last modified information associated with the . + /// + public DateTimeOffset? LastModified { get; internal set; } + + /// + /// Gets or sets the etag associated with the . + /// + public EntityTagHeaderValue? EntityTag { get; internal init; } + + /// + /// Gets or sets the value that enables range processing for the . + /// + public bool EnableRangeProcessing { get; internal init; } + + /// + /// Gets or sets the file length information associated with the . + /// + public long? FileLength { get; internal set; } + /// /// Gets or sets the path to the file that will be sent back as the response. /// @@ -41,30 +70,8 @@ public string FileName set => _fileName = value ?? throw new ArgumentNullException(nameof(value)); } - protected internal override Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) - { - var response = httpContext.Response; - var offset = 0L; - var count = (long?)null; - if (range != null) - { - offset = range.From ?? 0L; - count = rangeLength; - } - - return response.SendFileAsync( - _fileInfo!, - offset, - count); - } - - protected internal override ILogger GetLogger(HttpContext httpContext) - { - return httpContext.RequestServices.GetRequiredService>(); - } - /// - public override Task ExecuteAsync(HttpContext httpContext) + public Task ExecuteAsync(HttpContext httpContext) { var hostingEnvironment = httpContext.RequestServices.GetRequiredService(); @@ -78,7 +85,31 @@ public override Task ExecuteAsync(HttpContext httpContext) LastModified = LastModified ?? fileInfo.LastModified; FileLength = fileInfo.Length; - return base.ExecuteAsync(httpContext); + return HttpResultsWriter.WriteResultAsFileAsync(httpContext, + ExecuteCoreAsync, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag); + } + + private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + { + var response = httpContext.Response; + var offset = 0L; + var count = (long?)null; + if (range != null) + { + offset = range.From ?? 0L; + count = rangeLength; + } + + return response.SendFileAsync( + _fileInfo!, + offset, + count); } internal IFileInfo GetFileInformation(IFileProvider fileProvider) diff --git a/src/Http/Http.Results/test/FileContentResultTest.cs b/src/Http/Http.Results/test/FileContentResultTest.cs index ca197faeb4e0..1628bcc8da6d 100644 --- a/src/Http/Http.Results/test/FileContentResultTest.cs +++ b/src/Http/Http.Results/test/FileContentResultTest.cs @@ -28,6 +28,7 @@ protected override Task ExecuteAsync( httpContext.RequestServices = new ServiceCollection() .AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)) + .AddSingleton() .BuildServiceProvider(); return result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/LocalRedirectResultTest.cs b/src/Http/Http.Results/test/LocalRedirectResultTest.cs index 4731c93ddbc0..76970a4f1cf3 100644 --- a/src/Http/Http.Results/test/LocalRedirectResultTest.cs +++ b/src/Http/Http.Results/test/LocalRedirectResultTest.cs @@ -16,7 +16,7 @@ public void Constructor_WithParameterUrl_SetsResultUrlAndNotPermanentOrPreserveM var url = "/test/url"; // Act - var result = new LocalRedirectHttpResult(url); + var result = new RedirectHttpResult(url, acceptLocalUrlOnly: true); // Assert Assert.False(result.PreserveMethod); @@ -31,7 +31,7 @@ public void Constructor_WithParameterUrlAndPermanent_SetsResultUrlAndPermanentNo var url = "/test/url"; // Act - var result = new LocalRedirectHttpResult(url, permanent: true); + var result = new RedirectHttpResult(url, permanent: true, acceptLocalUrlOnly: true); // Assert Assert.False(result.PreserveMethod); @@ -46,7 +46,7 @@ public void Constructor_WithParameterUrlAndPermanent_SetsResultUrlPermanentAndPr var url = "/test/url"; // Act - var result = new LocalRedirectHttpResult(url, permanent: true, preserveMethod: true); + var result = new RedirectHttpResult(url, permanent: true, preserveMethod: true, acceptLocalUrlOnly: true); // Assert Assert.True(result.PreserveMethod); @@ -63,7 +63,7 @@ public async Task Execute_ReturnsExpectedValues() var expectedPath = "/Home/About"; var httpContext = GetHttpContext(appRoot); - var result = new LocalRedirectHttpResult(contentPath); + var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true); // Act await result.ExecuteAsync(httpContext); @@ -86,7 +86,7 @@ public async Task Execute_Throws_ForNonLocalUrl( { // Arrange var httpContext = GetHttpContext(appRoot); - var result = new LocalRedirectHttpResult(contentPath); + var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true); // Act & Assert var exception = await Assert.ThrowsAsync(() => result.ExecuteAsync(httpContext)); @@ -107,7 +107,7 @@ public async Task Execute_Throws_ForNonLocalUrlTilde( { // Arrange var httpContext = GetHttpContext(appRoot); - var result = new LocalRedirectHttpResult(contentPath); + var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true); // Act & Assert var exception = await Assert.ThrowsAsync(() => result.ExecuteAsync(httpContext)); From a1fef7585c6ebb8c4dd6d979bb677b7bb67b86b5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Mar 2022 01:32:18 -0700 Subject: [PATCH 08/29] Changing method name to WriteResultAsStatusCode --- src/Http/Http.Results/src/HttpResultsWriter.cs | 8 ++------ src/Http/Http.Results/src/NoContentHttpResult.cs | 2 +- src/Http/Http.Results/src/StatusCodeHttpResult.cs | 2 +- src/Http/Http.Results/src/UnauthorizedHttpResult.cs | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Http/Http.Results/src/HttpResultsWriter.cs b/src/Http/Http.Results/src/HttpResultsWriter.cs index 74f2a96707de..904bae70ff13 100644 --- a/src/Http/Http.Results/src/HttpResultsWriter.cs +++ b/src/Http/Http.Results/src/HttpResultsWriter.cs @@ -13,10 +13,8 @@ namespace Microsoft.AspNetCore.Http; internal static partial class HttpResultsWriter { - public static void SetHttpStatusCode(HttpContext httpContext, int statusCode) + public static void WriteResultAsStatusCode(HttpContext httpContext, int statusCode) { - // TODO: Check null httpContext - Log.StatusCodeResultExecuting(GetLogger(httpContext), statusCode); httpContext.Response.StatusCode = statusCode; } @@ -29,8 +27,6 @@ public static Task WriteResultAsJson( JsonSerializerOptions? jsonSerializerOptions = null, Action? responseHeader = null) { - // TODO: Check null httpContext - Log.ObjectResultExecuting(GetLogger(httpContext), value, statusCode); if (value is ProblemDetails problemDetails) @@ -41,7 +37,7 @@ public static Task WriteResultAsJson( if (statusCode is { } code) { - SetHttpStatusCode(httpContext, code); + WriteResultAsStatusCode(httpContext, code); } responseHeader?.Invoke(httpContext); diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index bcbc491248ab..baa8b5585748 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -21,7 +21,7 @@ internal NoContentHttpResult() public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.SetHttpStatusCode(httpContext, _statuCode); + HttpResultsWriter.WriteResultAsStatusCode(httpContext, _statuCode); return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index 86c7311dee03..26ea1a3c5d4e 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -33,7 +33,7 @@ internal StatusCodeHttpResult(int statusCode) /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.SetHttpStatusCode(httpContext, _statuCode); + HttpResultsWriter.WriteResultAsStatusCode(httpContext, _statuCode); return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index c61c387a5399..b8fa66f0fa5e 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -26,7 +26,7 @@ internal UnauthorizedHttpResult() /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.SetHttpStatusCode(httpContext, _statuCode); + HttpResultsWriter.WriteResultAsStatusCode(httpContext, _statuCode); return Task.CompletedTask; } From 1ee3138e63cf9b13e8d0336a550162fd9f347991 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Mar 2022 12:22:16 -0700 Subject: [PATCH 09/29] Updating API --- .../src/IAtLocationHttpResult.cs | 16 +++ .../src/IAtRouteHttpResult.cs | 23 ++++ .../Http.Abstractions/src/IFileHttpResult.cs | 42 ++++++ .../src/IObjectHttpResult.cs | 21 +++ .../src/IRedirectHttpResult.cs | 30 +++++ .../src/IStatusCodeHttpResult.cs | 16 +++ .../src/PublicAPI.Unshipped.txt | 21 +++ .../src/AcceptedAtRouteHttpResult.cs | 23 ++-- .../Http.Results/src/AcceptedHttpResult.cs | 18 +-- .../src/BadRequestObjectHttpResult.cs | 13 +- .../Http.Results/src/ChallengeHttpResult.cs | 6 +- .../src/ConflictObjectHttpResult.cs | 14 +- .../Http.Results/src/ContentHttpResult.cs | 9 +- .../src/CreatedAtRouteHttpResult.cs | 26 ++-- .../Http.Results/src/CreatedHttpResult.cs | 21 ++- .../Http.Results/src/FileContentHttpResult.cs | 44 ++----- .../Http.Results/src/FileStreamHttpResult.cs | 36 ++--- src/Http/Http.Results/src/ForbidHttpResult.cs | 25 ++-- .../Http.Results/src/HttpResultsWriter.cs | 112 +++++++--------- src/Http/Http.Results/src/Interfaces.cs | 59 --------- src/Http/Http.Results/src/JsonHttpResult.cs | 21 +-- .../Http.Results/src/NoContentHttpResult.cs | 14 +- .../src/NotFoundObjectHttpResult.cs | 14 +- src/Http/Http.Results/src/ObjectHttpResult.cs | 10 +- .../Http.Results/src/OkObjectHttpResult.cs | 14 +- .../src/PhysicalFileHttpResult.cs | 49 ++----- .../Http.Results/src/ProblemHttpResult.cs | 13 +- .../Http.Results/src/PublicAPI.Unshipped.txt | 124 ++++++++++++++++++ .../Http.Results/src/PushStreamHttpResult.cs | 42 ++---- .../Http.Results/src/RedirectHttpResult.cs | 28 ++-- .../src/RedirectToRouteHttpResult.cs | 46 +++---- src/Http/Http.Results/src/Results.cs | 4 +- .../Http.Results/src/SignOutHttpResult.cs | 11 +- .../Http.Results/src/StatusCodeHttpResult.cs | 14 +- .../src/UnauthorizedHttpResult.cs | 19 +-- .../UnprocessableEntityObjectHttpResult.cs | 14 +- .../Http.Results/src/VirtualFileHttpResult.cs | 42 ++---- .../Http.Results/test/ProblemResultTests.cs | 101 ++++++++++++++ 38 files changed, 677 insertions(+), 478 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs create mode 100644 src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs create mode 100644 src/Http/Http.Abstractions/src/IFileHttpResult.cs create mode 100644 src/Http/Http.Abstractions/src/IObjectHttpResult.cs create mode 100644 src/Http/Http.Abstractions/src/IRedirectHttpResult.cs create mode 100644 src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs delete mode 100644 src/Http/Http.Results/src/Interfaces.cs create mode 100644 src/Http/Http.Results/test/ProblemResultTests.cs diff --git a/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs b/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs new file mode 100644 index 000000000000..5e182f7f7e30 --- /dev/null +++ b/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// Defines a contract that represents the result of an HTTP result endpoint +/// that constains an . +/// +public interface IAtLocationHttpResult +{ + /// + /// Gets the location at which the status of the requested content can be monitored. + /// + string? Location { get; } +} diff --git a/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs b/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs new file mode 100644 index 000000000000..b9fdfb04c58b --- /dev/null +++ b/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Routing; + +namespace Microsoft.AspNetCore.Http; + +/// +/// Defines a contract that represents the result of an HTTP result endpoint +/// that constains an object route. +/// +public interface IAtRouteHttpResult +{ + /// + /// Gets the name of the route to use for generating the URL. + /// + string? RouteName { get; } + + /// + /// Gets the route data to use for generating the URL. + /// + RouteValueDictionary? RouteValues { get; } +} diff --git a/src/Http/Http.Abstractions/src/IFileHttpResult.cs b/src/Http/Http.Abstractions/src/IFileHttpResult.cs new file mode 100644 index 000000000000..9719b8a53e5a --- /dev/null +++ b/src/Http/Http.Abstractions/src/IFileHttpResult.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Net.Http.Headers; + +namespace Microsoft.AspNetCore.Http; + +/// +/// Defines a contract that represents the file result of an HTTP result endpoint. +/// +public interface IFileHttpResult +{ + /// + /// Gets the Content-Type header for the response. + /// + string ContentType { get; } + + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// + string? FileDownloadName { get; } + + /// + /// Gets or sets the last modified information associated with the . + /// + DateTimeOffset? LastModified { get; } + + /// + /// Gets or sets the etag associated with the . + /// + EntityTagHeaderValue? EntityTag { get; } + + /// + /// Gets or sets the value that enables range processing for the . + /// + bool EnableRangeProcessing { get; } + + /// + /// Gets or sets the file length information associated with the . + /// + long? FileLength { get; } +} diff --git a/src/Http/Http.Abstractions/src/IObjectHttpResult.cs b/src/Http/Http.Abstractions/src/IObjectHttpResult.cs new file mode 100644 index 000000000000..202c41e2a92a --- /dev/null +++ b/src/Http/Http.Abstractions/src/IObjectHttpResult.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// Defines a contract that represents the result of an HTTP endpoint +/// that contains an object and . +/// +public interface IObjectHttpResult +{ + /// + /// Gets or sets the object result. + /// + object? Value { get; } + + /// + /// Gets or sets the HTTP status code. + /// + int? StatusCode { get; } +} diff --git a/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs b/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs new file mode 100644 index 000000000000..a4b4283fba26 --- /dev/null +++ b/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// Defines a contract that represents the HTTP Redirect result of an HTTP result endpoint. +/// +public interface IRedirectHttpResult +{ + /// + /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. + /// + bool Permanent { get; } + + /// + /// Gets an indication that the redirect preserves the initial request method. + /// + bool PreserveMethod { get; } + + /// + /// Gets the URL to redirect to. + /// + string? Url { get; } + + /// + /// Gets an indication that only local URLs are accepted. + /// + bool AcceptLocalUrlOnly { get; } +} diff --git a/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs b/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs new file mode 100644 index 000000000000..b686e25c4a54 --- /dev/null +++ b/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// Defines a contract that represents the result of an HTTP endpoint +/// that contains a . +/// +public interface IStatusCodeHttpResult +{ + /// + /// Gets or sets the HTTP status code. + /// + int? StatusCode { get; } +} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index bee1f68cba6d..8d1c5fb41c7e 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,6 +1,27 @@ #nullable enable *REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string! Microsoft.AspNetCore.Http.EndpointMetadataCollection.GetRequiredMetadata() -> T! +Microsoft.AspNetCore.Http.IAtLocationHttpResult +Microsoft.AspNetCore.Http.IAtLocationHttpResult.Location.get -> string? +Microsoft.AspNetCore.Http.IAtRouteHttpResult +Microsoft.AspNetCore.Http.IAtRouteHttpResult.RouteName.get -> string? +Microsoft.AspNetCore.Http.IAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.IFileHttpResult +Microsoft.AspNetCore.Http.IFileHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.IFileHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.IFileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.IFileHttpResult.FileDownloadName.get -> string? +Microsoft.AspNetCore.Http.IFileHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.IObjectHttpResult +Microsoft.AspNetCore.Http.IObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.IObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.IRedirectHttpResult +Microsoft.AspNetCore.Http.IRedirectHttpResult.AcceptLocalUrlOnly.get -> bool +Microsoft.AspNetCore.Http.IRedirectHttpResult.Permanent.get -> bool +Microsoft.AspNetCore.Http.IRedirectHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.IRedirectHttpResult.Url.get -> string? +Microsoft.AspNetCore.Http.IStatusCodeHttpResult +Microsoft.AspNetCore.Http.IStatusCodeHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.RouteHandlerFilterContext.RouteHandlerFilterContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! parameters) -> void Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerFilterContext! context, System.Func>! next) -> System.Threading.Tasks.ValueTask Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index 0e8ecb963661..e4fcb296d2ca 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -8,7 +8,9 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; /// -/// +/// An that on execution will write an object to the response +/// with status code Accepted (202) and Location header. +/// Targets a registered route. /// public sealed class AcceptedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRouteHttpResult, IStatusCodeHttpResult { @@ -40,30 +42,21 @@ internal AcceptedAtRouteHttpResult( RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; } - /// - /// Gets or sets the name of the route to use for generating the URL. - /// + /// public string? RouteName { get; } - /// - /// Gets or sets the route data to use for generating the URL. - /// + /// public RouteValueDictionary? RouteValues { get; } - /// - /// - /// + /// public int? StatusCode => StatusCodes.Status202Accepted; /// - public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this, configureResponseHeader: ConfigureResponseHeaders); private void ConfigureResponseHeaders(HttpContext context) { diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index d950147cf2fc..41b4f21ad3d0 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -6,7 +6,9 @@ namespace Microsoft.AspNetCore.Http; using System.Threading.Tasks; /// -/// +/// An that on execution will write an object to the response +/// with status code Accepted (202) and Location header. +/// Targets a registered route. /// public sealed class AcceptedHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult, IAtLocationHttpResult { @@ -55,24 +57,18 @@ internal AcceptedHttpResult(Uri locationUri, object? value) } } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; } - /// - /// Gets or sets the HTTP status code. - /// + /// public int? StatusCode => StatusCodes.Status202Accepted; - /// - /// Gets the location at which the status of the requested content can be monitored. - /// + /// public string? Location { get; } /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this, configureResponseHeader: ConfigureResponseHeaders); private void ConfigureResponseHeaders(HttpContext context) { diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index 32d6231b75b6..4d2773037799 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -4,7 +4,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that on execution will write an object to the response +/// with Bad Request (400) status code. /// public sealed class BadRequestObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { @@ -13,17 +14,13 @@ internal BadRequestObjectHttpResult(object? error) Value = error; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; internal init; } - /// - /// - /// + /// public int? StatusCode => StatusCodes.Status400BadRequest; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); } diff --git a/src/Http/Http.Results/src/ChallengeHttpResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs index e3c73e1946c6..d893f5a796d6 100644 --- a/src/Http/Http.Results/src/ChallengeHttpResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -73,14 +73,14 @@ internal ChallengeHttpResult(string authenticationScheme, AuthenticationProperti /// challenge. internal ChallengeHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { - AuthenticationSchemes = authenticationSchemes; + AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; } /// /// Gets or sets the authentication schemes that are challenged. /// - public IList AuthenticationSchemes { get; internal init; } = Array.Empty(); + public IReadOnlyList AuthenticationSchemes { get; internal init; } = Array.Empty(); /// /// Gets or sets the used to perform the sign-out operation. @@ -109,7 +109,7 @@ public async Task ExecuteAsync(HttpContext httpContext) private static partial class Log { - public static void ChallengeResultExecuting(ILogger logger, IList authenticationSchemes) + public static void ChallengeResultExecuting(ILogger logger, IReadOnlyList authenticationSchemes) { if (logger.IsEnabled(LogLevel.Information)) { diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index b4c185bd4a22..d71da8811399 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -4,7 +4,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that on execution will write an object to the response +/// with Conflict (409) status code. /// public sealed class ConflictObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { @@ -13,16 +14,13 @@ internal ConflictObjectHttpResult(object? error) Value = error; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; internal init; } - /// - /// - /// + /// public int? StatusCode => StatusCodes.Status409Conflict; + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); } diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index 10d79047d63d..b78b2dccb8c5 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -9,9 +9,10 @@ namespace Microsoft.AspNetCore.Http; /// -/// An that when executed will produce a response with content. +/// An that when executed +/// will produce a response with content. /// -public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult, IContentHttpResult +public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult { private const string DefaultContentType = "text/plain; charset=utf-8"; private static readonly Encoding DefaultEncoding = Encoding.UTF8; @@ -30,9 +31,7 @@ internal ContentHttpResult() /// public string? ContentType { get; internal init; } - /// - /// - /// + /// public int? StatusCode { get; internal init; } /// diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index d9d4604e17d8..792d94e612f0 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -7,7 +7,9 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; /// -/// +/// An that on execution will write an object to the response +/// with status code Created (201) and Location header. +/// Targets a registered route. /// public sealed class CreatedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRouteHttpResult, IStatusCodeHttpResult { @@ -39,28 +41,24 @@ internal CreatedAtRouteHttpResult( RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; } - /// - /// Gets or sets the name of the route to use for generating the URL. - /// + /// public string? RouteName { get; } - /// - /// Gets or sets the route data to use for generating the URL. - /// + /// public RouteValueDictionary? RouteValues { get; } - /// - /// - /// + /// public int? StatusCode => StatusCodes.Status201Created; + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + => HttpResultsWriter.WriteResultAsJson( + httpContext, + objectHttpResult: this, + configureResponseHeader: ConfigureResponseHeaders); private void ConfigureResponseHeaders(HttpContext context) { diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index e8e47d4855a9..c1f2deb3f0ff 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -4,7 +4,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that on execution will write an object to the response +/// with status code Created (201) and Location header. /// public sealed class CreatedHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult, IAtLocationHttpResult { @@ -45,23 +46,21 @@ internal CreatedHttpResult(Uri locationUri, object? value) } } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; } - /// - /// Gets or sets the HTTP status code. - /// + /// public int? StatusCode => StatusCodes.Status201Created; - /// - /// Gets the location at which the status of the requested content can be monitored. - /// + /// public string? Location { get; } + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode, responseHeader: ConfigureResponseHeaders); + => HttpResultsWriter.WriteResultAsJson( + httpContext, + objectHttpResult: this, + configureResponseHeader: ConfigureResponseHeaders); private void ConfigureResponseHeaders(HttpContext context) { diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index ebe0fca2dfba..77419aaf8aa8 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -7,7 +7,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// Represents an that when executed will +/// write a file from the content to the response. /// public sealed partial class FileContentHttpResult : IResult, IFileHttpResult { @@ -25,51 +26,32 @@ internal FileContentHttpResult(ReadOnlyMemory fileContents, string? conten ContentType = contentType ?? "application/octet-stream"; } - /// - /// Gets the Content-Type header for the response. - /// + /// public string ContentType { get; internal set; } - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// + /// public string? FileDownloadName { get; internal set; } - /// - /// Gets or sets the last modified information associated with the . - /// + /// public DateTimeOffset? LastModified { get; internal set; } - /// - /// Gets or sets the etag associated with the . - /// + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// - /// Gets or sets the value that enables range processing for the . - /// + /// public bool EnableRangeProcessing { get; internal init; } - /// - /// Gets or sets the file length information associated with the . - /// + /// public long? FileLength { get; internal set; } /// /// Gets or sets the file contents. /// - public ReadOnlyMemory FileContents { get; init; } + public ReadOnlyMemory FileContents { get; internal init; } /// - public Task ExecuteAsync(HttpContext httpContext) - { - return HttpResultsWriter.WriteResultAsFileAsync(httpContext, - (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength), - FileDownloadName, - FileLength, - ContentType, - EnableRangeProcessing, - LastModified, - EntityTag); - } + public Task ExecuteAsync(HttpContext httpContext) => HttpResultsWriter.WriteResultAsFileAsync( + httpContext, + fileHttpResult: this, + (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength)); } diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index df685dbdb2ae..88dfd47f8a57 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -34,34 +34,22 @@ internal FileStreamHttpResult(Stream fileStream, string? contentType) ContentType = contentType ?? "application/octet-stream"; } - /// - /// Gets the Content-Type header for the response. - /// + /// public string ContentType { get; internal set; } - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// + /// public string? FileDownloadName { get; internal set; } - /// - /// Gets or sets the last modified information associated with the . - /// + /// public DateTimeOffset? LastModified { get; internal set; } - /// - /// Gets or sets the etag associated with the . - /// + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// - /// Gets or sets the value that enables range processing for the . - /// + /// public bool EnableRangeProcessing { get; internal init; } - /// - /// Gets or sets the file length information associated with the . - /// + /// public long? FileLength { get; internal set; } /// @@ -74,14 +62,10 @@ public async Task ExecuteAsync(HttpContext httpContext) { await using (FileStream) { - await HttpResultsWriter.WriteResultAsFileAsync(httpContext, - (context, range, rangeLength) => FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength), - FileDownloadName, - FileLength, - ContentType, - EnableRangeProcessing, - LastModified, - EntityTag); + await HttpResultsWriter.WriteResultAsFileAsync( + httpContext, + fileHttpResult: this, + (context, range, rangeLength) => FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength)); } } } diff --git a/src/Http/Http.Results/src/ForbidHttpResult.cs b/src/Http/Http.Results/src/ForbidHttpResult.cs index fd3d0676df34..1f28664a5209 100644 --- a/src/Http/Http.Results/src/ForbidHttpResult.cs +++ b/src/Http/Http.Results/src/ForbidHttpResult.cs @@ -8,12 +8,15 @@ namespace Microsoft.AspNetCore.Http; -internal sealed partial class ForbidHttpResult : IResult +/// +/// An that on execution invokes . +/// +public sealed partial class ForbidHttpResult : IResult { /// /// Initializes a new instance of . /// - public ForbidHttpResult() + internal ForbidHttpResult() : this(Array.Empty()) { } @@ -23,7 +26,7 @@ public ForbidHttpResult() /// specified authentication scheme. /// /// The authentication scheme to challenge. - public ForbidHttpResult(string authenticationScheme) + internal ForbidHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -33,7 +36,7 @@ public ForbidHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to challenge. - public ForbidHttpResult(IList authenticationSchemes) + internal ForbidHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -44,7 +47,7 @@ public ForbidHttpResult(IList authenticationSchemes) /// /// used to perform the authentication /// challenge. - public ForbidHttpResult(AuthenticationProperties? properties) + internal ForbidHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } @@ -56,7 +59,7 @@ public ForbidHttpResult(AuthenticationProperties? properties) /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - public ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) + internal ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -68,21 +71,21 @@ public ForbidHttpResult(string authenticationScheme, AuthenticationProperties? p /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - public ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + internal ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { - AuthenticationSchemes = authenticationSchemes; + AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; } /// /// Gets or sets the authentication schemes that are challenged. /// - public IList AuthenticationSchemes { get; init; } + public IReadOnlyList AuthenticationSchemes { get; internal init; } /// /// Gets or sets the used to perform the authentication challenge. /// - public AuthenticationProperties? Properties { get; init; } + public AuthenticationProperties? Properties { get; internal init; } /// public async Task ExecuteAsync(HttpContext httpContext) @@ -106,7 +109,7 @@ public async Task ExecuteAsync(HttpContext httpContext) private static partial class Log { - public static void ForbidResultExecuting(ILogger logger, IList authenticationSchemes) + public static void ForbidResultExecuting(ILogger logger, IReadOnlyList authenticationSchemes) { if (logger.IsEnabled(LogLevel.Information)) { diff --git a/src/Http/Http.Results/src/HttpResultsWriter.cs b/src/Http/Http.Results/src/HttpResultsWriter.cs index 904bae70ff13..b66ddd865c7b 100644 --- a/src/Http/Http.Results/src/HttpResultsWriter.cs +++ b/src/Http/Http.Results/src/HttpResultsWriter.cs @@ -13,23 +13,29 @@ namespace Microsoft.AspNetCore.Http; internal static partial class HttpResultsWriter { - public static void WriteResultAsStatusCode(HttpContext httpContext, int statusCode) + public static void WriteResultAsStatusCode( + HttpContext httpContext, + IStatusCodeHttpResult statusCodeHttpResult) { - Log.StatusCodeResultExecuting(GetLogger(httpContext), statusCode); - httpContext.Response.StatusCode = statusCode; + if (statusCodeHttpResult is { StatusCode: { } statusCode }) + { + Log.WritingResultAsStatusCode(GetLogger(httpContext), statusCodeResultType: statusCodeHttpResult.GetType().Name, statusCode); + httpContext.Response.StatusCode = statusCode; + } } public static Task WriteResultAsJson( HttpContext httpContext, - object? value, + IObjectHttpResult objectHttpResult, string? contentType = null, - int? statusCode = null, JsonSerializerOptions? jsonSerializerOptions = null, - Action? responseHeader = null) + Action? configureResponseHeader = null) { - Log.ObjectResultExecuting(GetLogger(httpContext), value, statusCode); + Log.WritingResultAsJson(GetLogger(httpContext), objectHttpResult); + + var statusCode = objectHttpResult.StatusCode; - if (value is ProblemDetails problemDetails) + if (objectHttpResult.Value is ProblemDetails problemDetails) { ApplyProblemDetailsDefaults(problemDetails, statusCode); statusCode ??= problemDetails.Status; @@ -37,49 +43,48 @@ public static Task WriteResultAsJson( if (statusCode is { } code) { - WriteResultAsStatusCode(httpContext, code); + httpContext.Response.StatusCode = code; } - responseHeader?.Invoke(httpContext); + configureResponseHeader?.Invoke(httpContext); - if (value is null) + if (objectHttpResult.Value is null) { return Task.CompletedTask; } - return httpContext.Response.WriteAsJsonAsync(value, value.GetType(), options: jsonSerializerOptions, contentType: contentType); + return httpContext.Response.WriteAsJsonAsync( + objectHttpResult.Value, + objectHttpResult.Value.GetType(), + options: jsonSerializerOptions, + contentType: contentType); } public static Task WriteResultAsFileAsync( HttpContext httpContext, - Func writeOperation, - string? fileDownloadName, - long? fileLength, - string contentType, - bool enableRangeProcessing, - DateTimeOffset? lastModified, - EntityTagHeaderValue? entityTag) + IFileHttpResult fileHttpResult, + Func writeOperation) { var logger = GetLogger(httpContext); - Log.ExecutingFileResult(logger, fileDownloadName); + Log.WritingResultAsFile(logger, fileHttpResult); var fileResultInfo = new FileResultInfo { - ContentType = contentType, - EnableRangeProcessing = enableRangeProcessing, - EntityTag = entityTag, - FileDownloadName = fileDownloadName ?? string.Empty, - LastModified = lastModified, + ContentType = fileHttpResult.ContentType, + EnableRangeProcessing = fileHttpResult.EnableRangeProcessing, + EntityTag = fileHttpResult.EntityTag, + FileDownloadName = fileHttpResult.FileDownloadName ?? string.Empty, + LastModified = fileHttpResult.LastModified, }; var (range, rangeLength, serveBody) = FileResultHelper.SetHeadersAndLog( httpContext, fileResultInfo, - fileLength, - enableRangeProcessing, - lastModified, - entityTag, + fileHttpResult.FileLength, + fileHttpResult.EnableRangeProcessing, + fileHttpResult.LastModified, + fileHttpResult.EntityTag, logger); if (!serveBody) @@ -135,62 +140,47 @@ private static ILogger GetLogger(HttpContext httpContext) private static partial class Log { - public static void ObjectResultExecuting(ILogger logger, object? value, int? statusCode) + public static void WritingResultAsJson(ILogger logger, IObjectHttpResult objectHttpResult) { if (logger.IsEnabled(LogLevel.Information)) { - if (value is null) + var objectResultType = objectHttpResult.GetType().Name; + + if (objectHttpResult.Value is null) { - ObjectResultExecutingWithoutValue(logger, statusCode ?? StatusCodes.Status200OK); + WritingResultAsJsonWithoutValue(logger, objectResultType, objectHttpResult.StatusCode ?? StatusCodes.Status200OK); } else { - var valueType = value.GetType().FullName!; - ObjectResultExecuting(logger, valueType, statusCode ?? StatusCodes.Status200OK); + var valueType = objectHttpResult.Value.GetType().FullName!; + WritingResultAsJson(logger, objectResultType, valueType, objectHttpResult.StatusCode ?? StatusCodes.Status200OK); } } } - public static void ExecutingFileResult(ILogger logger, string? fileDownloadName) + public static void WritingResultAsFile(ILogger logger, IFileHttpResult fileHttpResult) { if (logger.IsEnabled(LogLevel.Information)) { - //TODO fix - var fileResultType = "";// fileResult.GetType().Name; - ExecutingFileResultWithNoFileName(logger, fileResultType, fileDownloadName: fileDownloadName ?? string.Empty); - } - } - - public static void ExecutingFileResult(ILogger logger, string? fileDownloadName, string fileName) - { - if (logger.IsEnabled(LogLevel.Information)) - { - //TODO fix - var fileResultType = "";//fileResult.GetType().Name; - ExecutingFileResult(logger, fileResultType, fileName, fileDownloadName: fileDownloadName ?? string.Empty); + var fileResultType = fileHttpResult.GetType().Name; + WritingResultAsFileWithNoFileName(logger, fileResultType, fileDownloadName: fileHttpResult.FileDownloadName ?? string.Empty); } } [LoggerMessage(1, LogLevel.Information, - "Executing StatusCodeResult, setting HTTP status code {StatusCode}.", + "Executing '{StatusCodeResultType}', setting HTTP status code {StatusCode}.", EventName = "StatusCodeResultExecuting")] - public static partial void StatusCodeResultExecuting(ILogger logger, int statusCode); + public static partial void WritingResultAsStatusCode(ILogger logger, string statusCodeResultType, int statusCode); - [LoggerMessage(2, LogLevel.Information, "Writing value of type '{Type}' with status code '{StatusCode}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)] - private static partial void ObjectResultExecuting(ILogger logger, string type, int statusCode); + [LoggerMessage(2, LogLevel.Information, "Executing '{ObjectResultType}', writing value of type '{Type}' with status code '{StatusCode}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)] + private static partial void WritingResultAsJson(ILogger logger, string objectResultType, string type, int statusCode); - [LoggerMessage(3, LogLevel.Information, "Executing result with status code '{StatusCode}'.", EventName = "ObjectResultExecutingWithoutValue", SkipEnabledCheck = true)] - private static partial void ObjectResultExecutingWithoutValue(ILogger logger, int statusCode); + [LoggerMessage(3, LogLevel.Information, "Executing result '{ObjectResultType}' with status code '{StatusCode}'.", EventName = "ObjectResultExecutingWithoutValue", SkipEnabledCheck = true)] + private static partial void WritingResultAsJsonWithoutValue(ILogger logger, string objectResultType, int statusCode); [LoggerMessage(4, LogLevel.Information, "Executing {FileResultType}, sending file with download name '{FileDownloadName}'.", EventName = "ExecutingFileResultWithNoFileName", SkipEnabledCheck = true)] - private static partial void ExecutingFileResultWithNoFileName(ILogger logger, string fileResultType, string fileDownloadName); - - [LoggerMessage(5, LogLevel.Information, - "Executing {FileResultType}, sending file '{FileDownloadPath}' with download name '{FileDownloadName}'.", - EventName = "ExecutingFileResult", - SkipEnabledCheck = true)] - private static partial void ExecutingFileResult(ILogger logger, string fileResultType, string fileDownloadPath, string fileDownloadName); + private static partial void WritingResultAsFileWithNoFileName(ILogger logger, string fileResultType, string fileDownloadName); } } diff --git a/src/Http/Http.Results/src/Interfaces.cs b/src/Http/Http.Results/src/Interfaces.cs deleted file mode 100644 index 29f4c6f4117c..000000000000 --- a/src/Http/Http.Results/src/Interfaces.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; -using Microsoft.Net.Http.Headers; - -public interface IStatusCodeHttpResult -{ - public int? StatusCode { get; } -} - -public interface IObjectHttpResult -{ - public object? Value { get; } -} - -public interface IProblemHttpResult -{ - public ProblemDetails ProblemDetails { get; } - public string ContentType { get; } -} - -public interface IAtRouteHttpResult -{ - public string? RouteName { get; } - public RouteValueDictionary? RouteValues { get; } -} - -public interface IAtLocationHttpResult -{ - public string? Location { get; } -} - -public interface IContentHttpResult -{ - public string? Content { get; } - public string? ContentType { get; } -} - -public interface IRedirectHttpResult -{ - public bool AcceptLocalUrlOnly { get; } - public bool Permanent { get; } - public bool PreserveMethod { get; } - public string? Url { get; } -} - -public interface IFileHttpResult -{ - public string ContentType { get; } - public string? FileDownloadName { get; } - public DateTimeOffset? LastModified { get; } - public EntityTagHeaderValue? EntityTag { get; } - public bool EnableRangeProcessing { get; } - public long? FileLength { get; } -} diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index 11d04c238fd6..9f54dc60dfbe 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -10,12 +10,12 @@ namespace Microsoft.AspNetCore.Http; /// public sealed class JsonHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { - public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) + internal JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) : this(value, statusCode: null, jsonSerializerOptions: jsonSerializerOptions) { } - public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) + internal JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) { Value = value; StatusCode = statusCode; @@ -31,11 +31,9 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso /// When using Newtonsoft.Json, this should be an instance of JsonSerializerSettings. /// /// - public JsonSerializerOptions? JsonSerializerOptions { get; init; } + public JsonSerializerOptions? JsonSerializerOptions { get; internal init; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; } /// @@ -43,11 +41,14 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso /// public string? ContentType { get; internal set; } - /// - /// - /// + /// public int? StatusCode { get; } + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, ContentType, StatusCode, JsonSerializerOptions); + => HttpResultsWriter.WriteResultAsJson( + httpContext, + objectHttpResult: this, + ContentType, + JsonSerializerOptions); } diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index baa8b5585748..a6bf9f7ceceb 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -4,24 +4,22 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// Represents an that when executed will +/// produce an HTTP response with the No Content (204) status code. /// public class NoContentHttpResult : IResult, IStatusCodeHttpResult { - private readonly int _statuCode = StatusCodes.Status204NoContent; - internal NoContentHttpResult() { } - /// - /// - /// - public int? StatusCode => _statuCode; + /// + public int? StatusCode => StatusCodes.Status204NoContent; + /// public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.WriteResultAsStatusCode(httpContext, _statuCode); + HttpResultsWriter.WriteResultAsStatusCode(httpContext, statusCodeHttpResult: this); return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index 4d716baf2b38..ccb703b7dad3 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -4,7 +4,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that on execution will write an object to the response +/// with Not Found (404) status code. /// public sealed class NotFoundObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { @@ -13,16 +14,13 @@ internal NotFoundObjectHttpResult(object? value) Value = value; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; internal init; } - /// - /// - /// + /// public int? StatusCode => StatusCodes.Status404NotFound; + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); } diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index 1f8d620cbabb..e7b584a99197 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -36,9 +36,7 @@ internal ObjectHttpResult(object? value, int? statusCode) StatusCode = statusCode; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; internal init; } /// @@ -46,9 +44,7 @@ internal ObjectHttpResult(object? value, int? statusCode) /// public string? ContentType { get; internal init; } - /// - /// - /// + /// public int? StatusCode { get; internal init; } /// @@ -57,5 +53,5 @@ internal ObjectHttpResult(object? value, int? statusCode) /// /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, ContentType, StatusCode); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this, ContentType); } diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index cdc1ac0d11ee..13cd2f2edcb9 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -6,7 +6,8 @@ namespace Microsoft.AspNetCore.Http; using System.Threading.Tasks; /// -/// +/// An that on execution will write an object to the response +/// with Ok (200) status code. /// public sealed class OkObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { @@ -15,16 +16,13 @@ internal OkObjectHttpResult(object? value) Value = value; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; internal init; } - /// - /// - /// + /// public int? StatusCode => StatusCodes.Status200OK; + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); } diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index 71bc0bc48850..e8314e6c003b 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -23,34 +23,22 @@ internal PhysicalFileHttpResult(string fileName, string? contentType) ContentType = contentType ?? "application/octet-stream"; } - /// - /// Gets the Content-Type header for the response. - /// - public string ContentType { get; internal set;} + /// + public string ContentType { get; internal set; } - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// - public string? FileDownloadName { get; internal set;} + /// + public string? FileDownloadName { get; internal set; } - /// - /// Gets or sets the last modified information associated with the . - /// + /// public DateTimeOffset? LastModified { get; internal set; } - /// - /// Gets or sets the etag associated with the . - /// + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// - /// Gets or sets the value that enables range processing for the . - /// + /// public bool EnableRangeProcessing { get; internal init; } - /// - /// Gets or sets the file length information associated with the . - /// + /// public long? FileLength { get; internal set; } /// @@ -62,12 +50,7 @@ internal PhysicalFileHttpResult(string fileName, string? contentType) internal Func GetFileInfoWrapper { get; init; } = static path => new FileInfoWrapper(path); - /// - /// - /// - /// - /// - /// + /// public Task ExecuteAsync(HttpContext httpContext) { var fileInfo = GetFileInfoWrapper(FileName); @@ -76,17 +59,13 @@ public Task ExecuteAsync(HttpContext httpContext) throw new FileNotFoundException($"Could not find file: {FileName}", FileName); } - LastModified = LastModified ?? fileInfo.LastWriteTimeUtc; + LastModified ??= fileInfo.LastWriteTimeUtc; FileLength = fileInfo.Length; - return HttpResultsWriter.WriteResultAsFileAsync(httpContext, - ExecuteCoreAsync, - FileDownloadName, - FileLength, - ContentType, - EnableRangeProcessing, - LastModified, - EntityTag); + return HttpResultsWriter.WriteResultAsFileAsync( + httpContext, + fileHttpResult: this, + writeOperation: ExecuteCoreAsync); } private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index ba5ec3acf6b9..71de00e980ee 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write Problem Details /// HTTP API responses based on https://tools.ietf.org/html/rfc7807 /// -public sealed class ProblemHttpResult : IResult, IProblemHttpResult +public sealed class ProblemHttpResult : IResult, IObjectHttpResult { internal ProblemHttpResult(ProblemDetails problemDetails) { @@ -26,7 +26,16 @@ internal ProblemHttpResult(ProblemDetails problemDetails) /// public string ContentType => "application/problem+json"; + /// + public object? Value => ProblemDetails; + + /// + public int? StatusCode => ProblemDetails.Status; + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, ProblemDetails, ContentType); + => HttpResultsWriter.WriteResultAsJson( + httpContext, + objectHttpResult: this, + ContentType); } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 6506d67f2e3a..9c901c3a474d 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -14,10 +14,134 @@ Microsoft.AspNetCore.Http.BadRequestObjectHttpResult Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.ChallengeHttpResult +Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.ConflictObjectHttpResult +Microsoft.AspNetCore.Http.ConflictObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.ConflictObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.ConflictObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.ContentHttpResult +Microsoft.AspNetCore.Http.ContentHttpResult.Content.get -> string? +Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.get -> string? +Microsoft.AspNetCore.Http.ContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.ContentHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.RouteName.get -> string? +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.CreatedHttpResult +Microsoft.AspNetCore.Http.CreatedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.CreatedHttpResult.Location.get -> string? +Microsoft.AspNetCore.Http.CreatedHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.CreatedHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.EmptyHttpResult +Microsoft.AspNetCore.Http.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.FileContentHttpResult +Microsoft.AspNetCore.Http.FileContentHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.FileContentHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.FileContentHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.FileContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.FileContentHttpResult.FileContents.get -> System.ReadOnlyMemory +Microsoft.AspNetCore.Http.FileContentHttpResult.FileDownloadName.get -> string? +Microsoft.AspNetCore.Http.FileContentHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.FileContentHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.FileStreamHttpResult +Microsoft.AspNetCore.Http.FileStreamHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.FileStreamHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.FileStreamHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.FileStreamHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.FileStreamHttpResult.FileDownloadName.get -> string? +Microsoft.AspNetCore.Http.FileStreamHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStream.get -> System.IO.Stream! +Microsoft.AspNetCore.Http.FileStreamHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.ForbidHttpResult +Microsoft.AspNetCore.Http.ForbidHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Http.ForbidHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.ForbidHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.JsonHttpResult +Microsoft.AspNetCore.Http.JsonHttpResult.ContentType.get -> string? +Microsoft.AspNetCore.Http.JsonHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.JsonHttpResult.JsonSerializerOptions.get -> System.Text.Json.JsonSerializerOptions? +Microsoft.AspNetCore.Http.JsonHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.JsonHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.NoContentHttpResult +Microsoft.AspNetCore.Http.NoContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.NoContentHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.NotFoundObjectHttpResult +Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.OkObjectHttpResult +Microsoft.AspNetCore.Http.OkObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.OkObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.OkObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.PhysicalFileHttpResult +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileDownloadName.get -> string? +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileName.get -> string! +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.LastModified.get -> System.DateTimeOffset? Microsoft.AspNetCore.Http.ProblemHttpResult Microsoft.AspNetCore.Http.ProblemHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.ProblemHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemDetails.get -> Microsoft.AspNetCore.Mvc.ProblemDetails! +Microsoft.AspNetCore.Http.ProblemHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.ProblemHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.PushStreamHttpResult +Microsoft.AspNetCore.Http.PushStreamHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.PushStreamHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.PushStreamHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.PushStreamHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.PushStreamHttpResult.FileDownloadName.get -> string? +Microsoft.AspNetCore.Http.PushStreamHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.PushStreamHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.RedirectHttpResult +Microsoft.AspNetCore.Http.RedirectHttpResult.AcceptLocalUrlOnly.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.RedirectHttpResult.Permanent.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string? +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.AcceptLocalUrlOnly.get -> bool +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Fragment.get -> string? +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Permanent.get -> bool +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteName.get -> string? +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Url.get -> string? +Microsoft.AspNetCore.Http.SignInHttpResult +Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.get -> string? +Microsoft.AspNetCore.Http.SignInHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.SignInHttpResult.Principal.get -> System.Security.Claims.ClaimsPrincipal! +Microsoft.AspNetCore.Http.SignInHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.SignOutHttpResult +Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Http.SignOutHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.UnauthorizedHttpResult +Microsoft.AspNetCore.Http.UnauthorizedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.UnauthorizedHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult +Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.Value.get -> object? +Microsoft.AspNetCore.Http.VirtualFileHttpResult +Microsoft.AspNetCore.Http.VirtualFileHttpResult.ContentType.get -> string! +Microsoft.AspNetCore.Http.VirtualFileHttpResult.EnableRangeProcessing.get -> bool +Microsoft.AspNetCore.Http.VirtualFileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? +Microsoft.AspNetCore.Http.VirtualFileHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileDownloadName.get -> string? +Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileName.get -> string! +Microsoft.AspNetCore.Http.VirtualFileHttpResult.LastModified.get -> System.DateTimeOffset? static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, bool enableRangeProcessing = false, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index fd55896c63d5..8f96e41bf43e 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -6,7 +6,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// Represents an that when executed will +/// write a file from the writer callback to the response. /// public sealed class PushStreamHttpResult : IResult, IFileHttpResult { @@ -18,46 +19,27 @@ internal PushStreamHttpResult(Func streamWriterCallback, string? c ContentType = contentType ?? "application/octet-stream"; } - /// - /// Gets the Content-Type header for the response. - /// + /// public string ContentType { get; internal set; } - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// + /// public string? FileDownloadName { get; internal set; } - /// - /// Gets or sets the last modified information associated with the . - /// + /// public DateTimeOffset? LastModified { get; internal set; } - /// - /// Gets or sets the etag associated with the . - /// + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// - /// Gets or sets the value that enables range processing for the . - /// + /// public bool EnableRangeProcessing { get; internal init; } - /// - /// Gets or sets the file length information associated with the . - /// + /// public long? FileLength { get; internal set; } /// - public Task ExecuteAsync(HttpContext httpContext) - { - return HttpResultsWriter.WriteResultAsFileAsync(httpContext, - (context, _, _) => _streamWriterCallback(httpContext.Response.Body), - FileDownloadName, - FileLength, - ContentType, - EnableRangeProcessing, - LastModified, - EntityTag); - } + public Task ExecuteAsync(HttpContext httpContext) => HttpResultsWriter.WriteResultAsFileAsync( + httpContext, + fileHttpResult: this, + (context, _, _) => _streamWriterCallback(httpContext.Response.Body)); } diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index 47a47169b8be..5fcfab0f80b1 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -20,7 +20,8 @@ public sealed partial class RedirectHttpResult : IResult, IRedirectHttpResult /// provided. /// /// The URL to redirect to. - /// If set to true, only local URLs are accepted and will throw an exception when the supplied URL is not considered local. (Default: false) + /// If set to true, only local URLs are accepted and + /// will throw an exception when the supplied URL is not considered local. (Default: false) internal RedirectHttpResult(string url, bool acceptLocalUrlOnly = false) : this(url, permanent: false, acceptLocalUrlOnly) { @@ -32,7 +33,8 @@ internal RedirectHttpResult(string url, bool acceptLocalUrlOnly = false) /// /// The URL to redirect to. /// Specifies whether the redirect should be permanent (301) or temporary (302). - /// If set to true, only local URLs are accepted and will throw an exception when the supplied URL is not considered local. (Default: false) + /// If set to true, only local URLs are accepted + /// and will throw an exception when the supplied URL is not considered local. (Default: false) internal RedirectHttpResult(string url, bool permanent, bool acceptLocalUrlOnly = false) : this(url, permanent, preserveMethod: false, acceptLocalUrlOnly) { @@ -44,8 +46,10 @@ internal RedirectHttpResult(string url, bool permanent, bool acceptLocalUrlOnly /// /// The URL to redirect to. /// Specifies whether the redirect should be permanent (301) or temporary (302). - /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. - /// If set to true, only local URLs are accepted and will throw an exception when the supplied URL is not considered local. (Default: false) + /// If set to true, make the temporary redirect (307) + /// or permanent redirect (308) preserve the initial request method. + /// If set to true, only local URLs are accepted + /// and will throw an exception when the supplied URL is not considered local. (Default: false) internal RedirectHttpResult(string url, bool permanent, bool preserveMethod, bool acceptLocalUrlOnly = false) { if (url == null) @@ -65,24 +69,16 @@ internal RedirectHttpResult(string url, bool permanent, bool preserveMethod, boo _url = url; } - /// - /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. - /// + /// public bool Permanent { get; } - /// - /// Gets an indication that the redirect preserves the initial request method. - /// + /// public bool PreserveMethod { get; } - /// - /// Gets the URL to redirect to. - /// + /// public string? Url => _url; - /// - /// Gets an indication that only local URLs are accepted. - /// + /// public bool AcceptLocalUrlOnly { get; } /// diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index 3eb71a319874..89bf304c6bc3 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -45,7 +45,8 @@ internal RedirectToRouteHttpResult( /// /// The name of the route. /// The parameters for the route. - /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). + /// If set to true, makes the redirect permanent (301). + /// Otherwise a temporary redirect is used (302). internal RedirectToRouteHttpResult( string? routeName, object? routeValues, @@ -60,8 +61,10 @@ internal RedirectToRouteHttpResult( /// /// The name of the route. /// The parameters for the route. - /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). - /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. + /// If set to true, makes the redirect permanent (301). + /// Otherwise a temporary redirect is used (302). + /// If set to true, make the temporary redirect (307) + /// or permanent redirect (308) preserve the initial request method. internal RedirectToRouteHttpResult( string? routeName, object? routeValues, @@ -92,7 +95,8 @@ internal RedirectToRouteHttpResult( /// /// The name of the route. /// The parameters for the route. - /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). + /// If set to true, makes the redirect permanent (301). + /// Otherwise a temporary redirect is used (302). /// The fragment to add to the URL. internal RedirectToRouteHttpResult( string? routeName, @@ -109,8 +113,10 @@ internal RedirectToRouteHttpResult( /// /// The name of the route. /// The parameters for the route. - /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). - /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. + /// If set to true, makes the redirect permanent (301). + /// Otherwise a temporary redirect is used (302). + /// If set to true, make the temporary redirect (307) + /// or permanent redirect (308) preserve the initial request method. /// The fragment to add to the URL. internal RedirectToRouteHttpResult( string? routeName, @@ -126,39 +132,25 @@ internal RedirectToRouteHttpResult( Fragment = fragment; } - /// - /// Gets the name of the route to use for generating the URL. - /// + /// public string? RouteName { get; } - /// - /// Gets the route data to use for generating the URL. - /// + /// public RouteValueDictionary? RouteValues { get; } - /// - /// Gets an indication that the redirect is permanent. - /// + /// public bool Permanent { get; } - /// - /// Gets an indication that the redirect preserves the initial request method. - /// + /// public bool PreserveMethod { get; } - /// - /// Gets the fragment to add to the URL. - /// + /// public string? Fragment { get; } - /// - /// Gets an indication that only local URLs are accepted. - /// + /// public bool AcceptLocalUrlOnly => true; - /// - /// Gets the URL to redirect to. - /// + /// public string? Url => _destinationUrl; /// diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 3d363aa6fe16..84f5920673df 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -32,7 +32,7 @@ public static class Results public static IResult Challenge( AuthenticationProperties? properties = null, IList? authenticationSchemes = null) - => new ChallengeHttpResult { AuthenticationSchemes = authenticationSchemes ?? Array.Empty(), Properties = properties }; + => new ChallengeHttpResult(authenticationSchemes: authenticationSchemes ?? Array.Empty(), properties); /// /// Creates a that on execution invokes . @@ -50,7 +50,7 @@ public static IResult Challenge( /// a redirect to show a login page. /// public static IResult Forbid(AuthenticationProperties? properties = null, IList? authenticationSchemes = null) - => new ForbidHttpResult { Properties = properties, AuthenticationSchemes = authenticationSchemes ?? Array.Empty(), }; + => new ForbidHttpResult(authenticationSchemes: authenticationSchemes ?? Array.Empty(), properties); /// /// Creates an that on execution invokes . diff --git a/src/Http/Http.Results/src/SignOutHttpResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs index 758eecc7a4c7..5bd79b39beb1 100644 --- a/src/Http/Http.Results/src/SignOutHttpResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -70,14 +70,19 @@ internal SignOutHttpResult(string authenticationScheme, AuthenticationProperties /// used to perform the sign-out operation. internal SignOutHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { - AuthenticationSchemes = authenticationSchemes ?? throw new ArgumentNullException(nameof(authenticationSchemes)); + if (authenticationSchemes is null) + { + throw new ArgumentNullException(nameof(authenticationSchemes)); + } + + AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; } /// /// Gets or sets the authentication schemes that are challenged. /// - public IList AuthenticationSchemes { get; internal init; } + public IReadOnlyList AuthenticationSchemes { get; internal init; } /// /// Gets or sets the used to perform the sign-out operation. @@ -106,7 +111,7 @@ public async Task ExecuteAsync(HttpContext httpContext) private static partial class Log { - public static void SignOutResultExecuting(ILogger logger, IList authenticationSchemes) + public static void SignOutResultExecuting(ILogger logger, IReadOnlyList authenticationSchemes) { if (logger.IsEnabled(LogLevel.Information)) { diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index 26ea1a3c5d4e..dfadbf9356b5 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -7,10 +7,8 @@ namespace Microsoft.AspNetCore.Http; /// Represents an that when executed will /// produce an HTTP response with the given response status code. /// -public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult +internal sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult { - private readonly int _statuCode; - /// /// Initializes a new instance of the class /// with the given . @@ -18,13 +16,11 @@ public sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResul /// The HTTP status code of the response. internal StatusCodeHttpResult(int statusCode) { - _statuCode = statusCode; + StatusCode = statusCode; } - /// - /// Gets or sets the HTTP status code. - /// - public int? StatusCode => _statuCode; + /// + public int? StatusCode { get; } /// /// Sets the status code on the HTTP response. @@ -33,7 +29,7 @@ internal StatusCodeHttpResult(int statusCode) /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.WriteResultAsStatusCode(httpContext, _statuCode); + HttpResultsWriter.WriteResultAsStatusCode(httpContext, statusCodeHttpResult: this); return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index b8fa66f0fa5e..58f4302476de 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -4,29 +4,22 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// Represents an that when executed will +/// produce an HTTP response with the No Unauthorized (401) status code. /// public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult { - private readonly int _statuCode = StatusCodes.Status401Unauthorized; - internal UnauthorizedHttpResult() { } - /// - /// Gets or sets the HTTP status code. - /// - public int? StatusCode => _statuCode; + /// + public int? StatusCode => StatusCodes.Status401Unauthorized; - /// - /// Sets the status code on the HTTP response. - /// - /// The for the current request. - /// A task that represents the asynchronous execute operation. + /// public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.WriteResultAsStatusCode(httpContext, _statuCode); + HttpResultsWriter.WriteResultAsStatusCode(httpContext, statusCodeHttpResult: this); return Task.CompletedTask; } diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index bbf1283f5c9f..7b99bc3fb107 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -4,7 +4,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// +/// An that on execution will write an object to the response +/// with Unprocessable Entity (422) status code. /// public sealed class UnprocessableEntityObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult { @@ -13,16 +14,13 @@ internal UnprocessableEntityObjectHttpResult(object? error) Value = error; } - /// - /// Gets or sets the object result. - /// + /// public object? Value { get; internal init; } - /// - /// - /// + /// > public int? StatusCode => StatusCodes.Status422UnprocessableEntity; + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, Value, statusCode: StatusCode); + => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); } diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index fee68eba7efb..26ba34976f80 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -10,8 +10,8 @@ namespace Microsoft.AspNetCore.Http; /// -/// A that on execution writes the file specified using a virtual path to the response -/// using mechanisms provided by the host. +/// A that on execution writes the file specified +/// using a virtual path to the response using mechanisms provided by the host. /// public sealed class VirtualFileHttpResult : IResult, IFileHttpResult { @@ -30,34 +30,22 @@ internal VirtualFileHttpResult(string fileName, string? contentType) ContentType = contentType ?? "application/octet-stream"; } - /// - /// Gets the Content-Type header for the response. - /// + /// public string ContentType { get; internal set; } - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// + /// public string? FileDownloadName { get; internal set; } - /// - /// Gets or sets the last modified information associated with the . - /// + /// public DateTimeOffset? LastModified { get; internal set; } - /// - /// Gets or sets the etag associated with the . - /// + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// - /// Gets or sets the value that enables range processing for the . - /// + /// public bool EnableRangeProcessing { get; internal init; } - /// - /// Gets or sets the file length information associated with the . - /// + /// public long? FileLength { get; internal set; } /// @@ -67,7 +55,7 @@ public string FileName { get => _fileName; [MemberNotNull(nameof(_fileName))] - set => _fileName = value ?? throw new ArgumentNullException(nameof(value)); + internal set => _fileName = value ?? throw new ArgumentNullException(nameof(value)); } /// @@ -85,14 +73,10 @@ public Task ExecuteAsync(HttpContext httpContext) LastModified = LastModified ?? fileInfo.LastModified; FileLength = fileInfo.Length; - return HttpResultsWriter.WriteResultAsFileAsync(httpContext, - ExecuteCoreAsync, - FileDownloadName, - FileLength, - ContentType, - EnableRangeProcessing, - LastModified, - EntityTag); + return HttpResultsWriter.WriteResultAsFileAsync( + httpContext, + fileHttpResult: this, + ExecuteCoreAsync); } private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) diff --git a/src/Http/Http.Results/test/ProblemResultTests.cs b/src/Http/Http.Results/test/ProblemResultTests.cs new file mode 100644 index 000000000000..349445009ed1 --- /dev/null +++ b/src/Http/Http.Results/test/ProblemResultTests.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.AspNetCore.Http.Result; + +public class ProblemResultTests +{ + [Fact] + public async Task ExecuteAsync_UsesDefaults_ForProblemDetails() + { + // Arrange + var details = new ProblemDetails(); + + var result = new ProblemHttpResult(details); + var stream = new MemoryStream(); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + Response = + { + Body = stream, + }, + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status500InternalServerError, httpContext.Response.StatusCode); + stream.Position = 0; + var responseDetails = JsonSerializer.Deserialize(stream); + Assert.Equal("https://tools.ietf.org/html/rfc7231#section-6.6.1", responseDetails.Type); + Assert.Equal("An error occurred while processing your request.", responseDetails.Title); + Assert.Equal(StatusCodes.Status500InternalServerError, responseDetails.Status); + } + + [Fact] + public async Task ExecuteAsync_UsesDefaults_ForValidationProblemDetails() + { + // Arrange + var details = new HttpValidationProblemDetails(); + + var result = new ProblemHttpResult(details); + var stream = new MemoryStream(); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + Response = + { + Body = stream, + }, + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, httpContext.Response.StatusCode); + stream.Position = 0; + var responseDetails = JsonSerializer.Deserialize(stream); + Assert.Equal("https://tools.ietf.org/html/rfc7231#section-6.5.1", responseDetails.Type); + Assert.Equal("One or more validation errors occurred.", responseDetails.Title); + Assert.Equal(StatusCodes.Status400BadRequest, responseDetails.Status); + } + + [Fact] + public async Task ExecuteAsync_GetsStatusCodeFromProblemDetails() + { + // Arrange + var details = new ProblemDetails { Status = StatusCodes.Status413RequestEntityTooLarge, }; + + var result = new ProblemHttpResult(details); + + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, details.Status.Value); + Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, result.StatusCode); + Assert.Equal(StatusCodes.Status413RequestEntityTooLarge, httpContext.Response.StatusCode); + } + + private static IServiceProvider CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(NullLoggerFactory.Instance); + + return services.BuildServiceProvider(); + } +} From 2c029a8e5cc9c46a0d3cbce99bb40a41b8449ffc Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Mar 2022 15:07:57 -0700 Subject: [PATCH 10/29] code cleanup --- src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs | 2 +- src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs | 2 +- src/Http/Http.Results/src/PushStreamHttpResult.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index e4fcb296d2ca..f2c3b1736241 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -39,7 +39,7 @@ internal AcceptedAtRouteHttpResult( { Value = value; RouteName = routeName; - RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); + RouteValues = new RouteValueDictionary(routeValues); } /// diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index 792d94e612f0..fae86a488170 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -38,7 +38,7 @@ internal CreatedAtRouteHttpResult( { Value = value; RouteName = routeName; - RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); + RouteValues = new RouteValueDictionary(routeValues); } /// diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index 8f96e41bf43e..b5315fe02b65 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -41,5 +41,5 @@ internal PushStreamHttpResult(Func streamWriterCallback, string? c public Task ExecuteAsync(HttpContext httpContext) => HttpResultsWriter.WriteResultAsFileAsync( httpContext, fileHttpResult: this, - (context, _, _) => _streamWriterCallback(httpContext.Response.Body)); + (context, _, _) => _streamWriterCallback(context.Response.Body)); } From 4103a0f92c64d66486ddfa8ffad62a649c2c39b9 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Mar 2022 17:31:05 -0700 Subject: [PATCH 11/29] fixing public api --- src/Http/Http.Abstractions/src/IFileHttpResult.cs | 2 +- src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/Http.Abstractions/src/IFileHttpResult.cs b/src/Http/Http.Abstractions/src/IFileHttpResult.cs index 9719b8a53e5a..6075737b1d71 100644 --- a/src/Http/Http.Abstractions/src/IFileHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IFileHttpResult.cs @@ -26,7 +26,7 @@ public interface IFileHttpResult DateTimeOffset? LastModified { get; } /// - /// Gets or sets the etag associated with the . + /// Gets or sets the etag associated with the . /// EntityTagHeaderValue? EntityTag { get; } diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 8d1c5fb41c7e..2401c5a75c4d 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -12,6 +12,7 @@ Microsoft.AspNetCore.Http.IFileHttpResult.EnableRangeProcessing.get -> bool Microsoft.AspNetCore.Http.IFileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? Microsoft.AspNetCore.Http.IFileHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.IFileHttpResult.FileLength.get -> long? +Microsoft.AspNetCore.Http.IFileHttpResult.LastModified.get -> System.DateTimeOffset? Microsoft.AspNetCore.Http.IObjectHttpResult Microsoft.AspNetCore.Http.IObjectHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.IObjectHttpResult.Value.get -> object? From 5fdebc4c42ccbe30e08c1491046de2701a16e92f Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Tue, 15 Mar 2022 13:59:03 -0700 Subject: [PATCH 12/29] Simplyfing interfaces --- src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs | 2 +- src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs | 2 +- src/Http/Http.Abstractions/src/IFileHttpResult.cs | 2 +- src/Http/Http.Abstractions/src/IObjectHttpResult.cs | 9 ++------- src/Http/Http.Abstractions/src/IRedirectHttpResult.cs | 2 +- src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs | 2 +- src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt | 1 - 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs b/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs index 5e182f7f7e30..1727b6f3928f 100644 --- a/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; /// Defines a contract that represents the result of an HTTP result endpoint /// that constains an . /// -public interface IAtLocationHttpResult +public interface IAtLocationHttpResult : IResult { /// /// Gets the location at which the status of the requested content can be monitored. diff --git a/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs b/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs index b9fdfb04c58b..e1315024a58f 100644 --- a/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Http; /// Defines a contract that represents the result of an HTTP result endpoint /// that constains an object route. /// -public interface IAtRouteHttpResult +public interface IAtRouteHttpResult : IResult { /// /// Gets the name of the route to use for generating the URL. diff --git a/src/Http/Http.Abstractions/src/IFileHttpResult.cs b/src/Http/Http.Abstractions/src/IFileHttpResult.cs index 6075737b1d71..d926ac6cc17f 100644 --- a/src/Http/Http.Abstractions/src/IFileHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IFileHttpResult.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Http; /// /// Defines a contract that represents the file result of an HTTP result endpoint. /// -public interface IFileHttpResult +public interface IFileHttpResult : IResult { /// /// Gets the Content-Type header for the response. diff --git a/src/Http/Http.Abstractions/src/IObjectHttpResult.cs b/src/Http/Http.Abstractions/src/IObjectHttpResult.cs index 202c41e2a92a..5bbec4022b48 100644 --- a/src/Http/Http.Abstractions/src/IObjectHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IObjectHttpResult.cs @@ -5,17 +5,12 @@ namespace Microsoft.AspNetCore.Http; /// /// Defines a contract that represents the result of an HTTP endpoint -/// that contains an object and . +/// that contains an object and a given statu code. /// -public interface IObjectHttpResult +public interface IObjectHttpResult : IResult, IStatusCodeHttpResult { /// /// Gets or sets the object result. /// object? Value { get; } - - /// - /// Gets or sets the HTTP status code. - /// - int? StatusCode { get; } } diff --git a/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs b/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs index a4b4283fba26..8cb5006a0c8f 100644 --- a/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Http; /// /// Defines a contract that represents the HTTP Redirect result of an HTTP result endpoint. /// -public interface IRedirectHttpResult +public interface IRedirectHttpResult : IResult { /// /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. diff --git a/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs b/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs index b686e25c4a54..f169abf1948e 100644 --- a/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs +++ b/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; /// Defines a contract that represents the result of an HTTP endpoint /// that contains a . /// -public interface IStatusCodeHttpResult +public interface IStatusCodeHttpResult : IResult { /// /// Gets or sets the HTTP status code. diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 2401c5a75c4d..baff64f08df6 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -14,7 +14,6 @@ Microsoft.AspNetCore.Http.IFileHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.IFileHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.IFileHttpResult.LastModified.get -> System.DateTimeOffset? Microsoft.AspNetCore.Http.IObjectHttpResult -Microsoft.AspNetCore.Http.IObjectHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.IObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.IRedirectHttpResult Microsoft.AspNetCore.Http.IRedirectHttpResult.AcceptLocalUrlOnly.get -> bool From 6146e4a76ed3fac9f484e74907c34115bc71c1c4 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 00:36:36 -0700 Subject: [PATCH 13/29] Removing interfaces + Adding ctors --- .../src/IAtLocationHttpResult.cs | 16 -- .../src/IAtRouteHttpResult.cs | 23 --- .../Http.Abstractions/src/IFileHttpResult.cs | 42 ------ .../src/IObjectHttpResult.cs | 16 -- .../src/IRedirectHttpResult.cs | 30 ---- .../src/IStatusCodeHttpResult.cs | 16 -- .../src/PublicAPI.Unshipped.txt | 21 --- .../src/AcceptedAtRouteHttpResult.cs | 10 +- .../Http.Results/src/AcceptedHttpResult.cs | 34 ++--- .../src/BadRequestObjectHttpResult.cs | 13 +- .../Http.Results/src/ChallengeHttpResult.cs | 12 +- .../src/ConflictObjectHttpResult.cs | 11 +- .../Http.Results/src/ContentHttpResult.cs | 66 +++------ .../src/CreatedAtRouteHttpResult.cs | 13 +- .../Http.Results/src/CreatedHttpResult.cs | 29 ++-- .../Http.Results/src/FileContentHttpResult.cs | 58 +++++++- .../Http.Results/src/FileStreamHttpResult.cs | 52 ++++++- src/Http/Http.Results/src/ForbidHttpResult.cs | 12 +- .../Http.Results/src/HttpResultsWriter.cs | 139 +++++++++++------- src/Http/Http.Results/src/JsonHttpResult.cs | 28 ++-- .../Http.Results/src/NoContentHttpResult.cs | 18 ++- .../src/NotFoundObjectHttpResult.cs | 10 +- src/Http/Http.Results/src/ObjectHttpResult.cs | 26 ++-- .../Http.Results/src/OkObjectHttpResult.cs | 12 +- .../src/PhysicalFileHttpResult.cs | 51 ++++++- .../Http.Results/src/ProblemHttpResult.cs | 17 ++- .../Http.Results/src/PublicAPI.Unshipped.txt | 92 ++++++++++-- .../Http.Results/src/PushStreamHttpResult.cs | 60 +++++++- .../Http.Results/src/RedirectHttpResult.cs | 30 ++-- .../src/RedirectToRouteHttpResult.cs | 34 ++--- src/Http/Http.Results/src/Results.cs | 26 ++-- src/Http/Http.Results/src/SignInHttpResult.cs | 30 +--- .../Http.Results/src/SignOutHttpResult.cs | 12 +- .../Http.Results/src/StatusCodeHttpResult.cs | 13 +- .../src/UnauthorizedHttpResult.cs | 18 ++- .../UnprocessableEntityObjectHttpResult.cs | 13 +- .../Http.Results/src/VirtualFileHttpResult.cs | 57 ++++++- .../Http.Results/test/ContentResultTest.cs | 18 +-- .../test/LocalRedirectResultTest.cs | 12 +- .../Http.Results/test/NoContentResultTests.cs | 55 +++++++ .../Http.Results/test/SignInResultTest.cs | 4 +- .../test/StatusCodeResultTests.cs | 1 + .../test/UnauthorizedResultTests.cs | 37 +++++ 43 files changed, 777 insertions(+), 510 deletions(-) delete mode 100644 src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs delete mode 100644 src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs delete mode 100644 src/Http/Http.Abstractions/src/IFileHttpResult.cs delete mode 100644 src/Http/Http.Abstractions/src/IObjectHttpResult.cs delete mode 100644 src/Http/Http.Abstractions/src/IRedirectHttpResult.cs delete mode 100644 src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs create mode 100644 src/Http/Http.Results/test/NoContentResultTests.cs diff --git a/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs b/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs deleted file mode 100644 index 1727b6f3928f..000000000000 --- a/src/Http/Http.Abstractions/src/IAtLocationHttpResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// Defines a contract that represents the result of an HTTP result endpoint -/// that constains an . -/// -public interface IAtLocationHttpResult : IResult -{ - /// - /// Gets the location at which the status of the requested content can be monitored. - /// - string? Location { get; } -} diff --git a/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs b/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs deleted file mode 100644 index e1315024a58f..000000000000 --- a/src/Http/Http.Abstractions/src/IAtRouteHttpResult.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Routing; - -namespace Microsoft.AspNetCore.Http; - -/// -/// Defines a contract that represents the result of an HTTP result endpoint -/// that constains an object route. -/// -public interface IAtRouteHttpResult : IResult -{ - /// - /// Gets the name of the route to use for generating the URL. - /// - string? RouteName { get; } - - /// - /// Gets the route data to use for generating the URL. - /// - RouteValueDictionary? RouteValues { get; } -} diff --git a/src/Http/Http.Abstractions/src/IFileHttpResult.cs b/src/Http/Http.Abstractions/src/IFileHttpResult.cs deleted file mode 100644 index d926ac6cc17f..000000000000 --- a/src/Http/Http.Abstractions/src/IFileHttpResult.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Net.Http.Headers; - -namespace Microsoft.AspNetCore.Http; - -/// -/// Defines a contract that represents the file result of an HTTP result endpoint. -/// -public interface IFileHttpResult : IResult -{ - /// - /// Gets the Content-Type header for the response. - /// - string ContentType { get; } - - /// - /// Gets the file name that will be used in the Content-Disposition header of the response. - /// - string? FileDownloadName { get; } - - /// - /// Gets or sets the last modified information associated with the . - /// - DateTimeOffset? LastModified { get; } - - /// - /// Gets or sets the etag associated with the . - /// - EntityTagHeaderValue? EntityTag { get; } - - /// - /// Gets or sets the value that enables range processing for the . - /// - bool EnableRangeProcessing { get; } - - /// - /// Gets or sets the file length information associated with the . - /// - long? FileLength { get; } -} diff --git a/src/Http/Http.Abstractions/src/IObjectHttpResult.cs b/src/Http/Http.Abstractions/src/IObjectHttpResult.cs deleted file mode 100644 index 5bbec4022b48..000000000000 --- a/src/Http/Http.Abstractions/src/IObjectHttpResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// Defines a contract that represents the result of an HTTP endpoint -/// that contains an object and a given statu code. -/// -public interface IObjectHttpResult : IResult, IStatusCodeHttpResult -{ - /// - /// Gets or sets the object result. - /// - object? Value { get; } -} diff --git a/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs b/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs deleted file mode 100644 index 8cb5006a0c8f..000000000000 --- a/src/Http/Http.Abstractions/src/IRedirectHttpResult.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// Defines a contract that represents the HTTP Redirect result of an HTTP result endpoint. -/// -public interface IRedirectHttpResult : IResult -{ - /// - /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. - /// - bool Permanent { get; } - - /// - /// Gets an indication that the redirect preserves the initial request method. - /// - bool PreserveMethod { get; } - - /// - /// Gets the URL to redirect to. - /// - string? Url { get; } - - /// - /// Gets an indication that only local URLs are accepted. - /// - bool AcceptLocalUrlOnly { get; } -} diff --git a/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs b/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs deleted file mode 100644 index f169abf1948e..000000000000 --- a/src/Http/Http.Abstractions/src/IStatusCodeHttpResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// Defines a contract that represents the result of an HTTP endpoint -/// that contains a . -/// -public interface IStatusCodeHttpResult : IResult -{ - /// - /// Gets or sets the HTTP status code. - /// - int? StatusCode { get; } -} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index baff64f08df6..bee1f68cba6d 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,27 +1,6 @@ #nullable enable *REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string! Microsoft.AspNetCore.Http.EndpointMetadataCollection.GetRequiredMetadata() -> T! -Microsoft.AspNetCore.Http.IAtLocationHttpResult -Microsoft.AspNetCore.Http.IAtLocationHttpResult.Location.get -> string? -Microsoft.AspNetCore.Http.IAtRouteHttpResult -Microsoft.AspNetCore.Http.IAtRouteHttpResult.RouteName.get -> string? -Microsoft.AspNetCore.Http.IAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? -Microsoft.AspNetCore.Http.IFileHttpResult -Microsoft.AspNetCore.Http.IFileHttpResult.ContentType.get -> string! -Microsoft.AspNetCore.Http.IFileHttpResult.EnableRangeProcessing.get -> bool -Microsoft.AspNetCore.Http.IFileHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? -Microsoft.AspNetCore.Http.IFileHttpResult.FileDownloadName.get -> string? -Microsoft.AspNetCore.Http.IFileHttpResult.FileLength.get -> long? -Microsoft.AspNetCore.Http.IFileHttpResult.LastModified.get -> System.DateTimeOffset? -Microsoft.AspNetCore.Http.IObjectHttpResult -Microsoft.AspNetCore.Http.IObjectHttpResult.Value.get -> object? -Microsoft.AspNetCore.Http.IRedirectHttpResult -Microsoft.AspNetCore.Http.IRedirectHttpResult.AcceptLocalUrlOnly.get -> bool -Microsoft.AspNetCore.Http.IRedirectHttpResult.Permanent.get -> bool -Microsoft.AspNetCore.Http.IRedirectHttpResult.PreserveMethod.get -> bool -Microsoft.AspNetCore.Http.IRedirectHttpResult.Url.get -> string? -Microsoft.AspNetCore.Http.IStatusCodeHttpResult -Microsoft.AspNetCore.Http.IStatusCodeHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.RouteHandlerFilterContext.RouteHandlerFilterContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! parameters) -> void Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerFilterContext! context, System.Func>! next) -> System.Threading.Tasks.ValueTask Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index f2c3b1736241..5380636e7e4e 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Http; /// with status code Accepted (202) and Location header. /// Targets a registered route. /// -public sealed class AcceptedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRouteHttpResult, IStatusCodeHttpResult +public sealed class AcceptedAtRouteHttpResult : IResult { /// /// Initializes a new instance of the class with the values @@ -20,7 +20,7 @@ public sealed class AcceptedAtRouteHttpResult : IResult, IObjectHttpResult, IAtR /// /// The route data to use for generating the URL. /// The value to format in the entity body. - internal AcceptedAtRouteHttpResult(object? routeValues, object? value) + public AcceptedAtRouteHttpResult(object? routeValues, object? value) : this(routeName: null, routeValues: routeValues, value: value) { } @@ -32,7 +32,7 @@ internal AcceptedAtRouteHttpResult(object? routeValues, object? value) /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - internal AcceptedAtRouteHttpResult( + public AcceptedAtRouteHttpResult( string? routeName, object? routeValues, object? value) @@ -52,11 +52,11 @@ internal AcceptedAtRouteHttpResult( public RouteValueDictionary? RouteValues { get; } /// - public int? StatusCode => StatusCodes.Status202Accepted; + public int StatusCode => StatusCodes.Status202Accepted; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this, configureResponseHeader: ConfigureResponseHeaders); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode, configureResponseHeader: ConfigureResponseHeaders); private void ConfigureResponseHeaders(HttpContext context) { diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index 41b4f21ad3d0..e610dd979940 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -10,23 +10,15 @@ namespace Microsoft.AspNetCore.Http; /// with status code Accepted (202) and Location header. /// Targets a registered route. /// -public sealed class AcceptedHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult, IAtLocationHttpResult +public sealed class AcceptedHttpResult : IResult { - /// - /// Initializes a new instance of the class with the values - /// provided. - /// - internal AcceptedHttpResult() - { - } - /// /// Initializes a new instance of the class with the values /// provided. /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - internal AcceptedHttpResult(string? location, object? value) + public AcceptedHttpResult(string? location, object? value) { Value = value; Location = location; @@ -38,7 +30,7 @@ internal AcceptedHttpResult(string? location, object? value) /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - internal AcceptedHttpResult(Uri locationUri, object? value) + public AcceptedHttpResult(Uri locationUri, object? value) { Value = value; @@ -61,20 +53,24 @@ internal AcceptedHttpResult(Uri locationUri, object? value) public object? Value { get; } /// - public int? StatusCode => StatusCodes.Status202Accepted; + public int StatusCode => StatusCodes.Status202Accepted; /// public string? Location { get; } /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this, configureResponseHeader: ConfigureResponseHeaders); - - private void ConfigureResponseHeaders(HttpContext context) { - if (!string.IsNullOrEmpty(Location)) - { - context.Response.Headers.Location = Location; - } + return HttpResultsWriter.WriteResultAsJsonAsync( + httpContext, + Value, + StatusCode, + configureResponseHeader: (context) => + { + if (!string.IsNullOrEmpty(Location)) + { + context.Response.Headers.Location = Location; + } + }); } } diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index 4d2773037799..2beefab21428 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -7,9 +7,14 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write an object to the response /// with Bad Request (400) status code. /// -public sealed class BadRequestObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +public sealed class BadRequestObjectHttpResult : IResult { - internal BadRequestObjectHttpResult(object? error) + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The error content to format in the entity body. + public BadRequestObjectHttpResult(object? error) { Value = error; } @@ -18,9 +23,9 @@ internal BadRequestObjectHttpResult(object? error) public object? Value { get; internal init; } /// - public int? StatusCode => StatusCodes.Status400BadRequest; + public int StatusCode => StatusCodes.Status400BadRequest; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/ChallengeHttpResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs index d893f5a796d6..2c5f3e904370 100644 --- a/src/Http/Http.Results/src/ChallengeHttpResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -16,7 +16,7 @@ public sealed partial class ChallengeHttpResult : IResult /// /// Initializes a new instance of . /// - internal ChallengeHttpResult() + public ChallengeHttpResult() : this(Array.Empty()) { } @@ -26,7 +26,7 @@ internal ChallengeHttpResult() /// specified authentication scheme. /// /// The authentication scheme to challenge. - internal ChallengeHttpResult(string authenticationScheme) + public ChallengeHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -36,7 +36,7 @@ internal ChallengeHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to challenge. - internal ChallengeHttpResult(IList authenticationSchemes) + public ChallengeHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -47,7 +47,7 @@ internal ChallengeHttpResult(IList authenticationSchemes) /// /// used to perform the authentication /// challenge. - internal ChallengeHttpResult(AuthenticationProperties? properties) + public ChallengeHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } @@ -59,7 +59,7 @@ internal ChallengeHttpResult(AuthenticationProperties? properties) /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - internal ChallengeHttpResult(string authenticationScheme, AuthenticationProperties? properties) + public ChallengeHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -71,7 +71,7 @@ internal ChallengeHttpResult(string authenticationScheme, AuthenticationProperti /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - internal ChallengeHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + public ChallengeHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index d71da8811399..bbbb4edee533 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -7,8 +7,13 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write an object to the response /// with Conflict (409) status code. /// -public sealed class ConflictObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +public sealed class ConflictObjectHttpResult : IResult { + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The error content to format in the entity body. internal ConflictObjectHttpResult(object? error) { Value = error; @@ -18,9 +23,9 @@ internal ConflictObjectHttpResult(object? error) public object? Value { get; internal init; } /// - public int? StatusCode => StatusCodes.Status409Conflict; + public int StatusCode => StatusCodes.Status409Conflict; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index b78b2dccb8c5..fef5cccac177 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -1,24 +1,35 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Text; -using Microsoft.AspNetCore.Internal; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - namespace Microsoft.AspNetCore.Http; /// /// An that when executed /// will produce a response with content. /// -public sealed partial class ContentHttpResult : IResult, IStatusCodeHttpResult +public sealed partial class ContentHttpResult : IResult { - private const string DefaultContentType = "text/plain; charset=utf-8"; - private static readonly Encoding DefaultEncoding = Encoding.UTF8; + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. + /// The Content-Type header for the response + public ContentHttpResult(string? content, string? contentType) + : this(content, contentType, statusCode: null) + { + } - internal ContentHttpResult() + /// + /// Initializes a new instance of the class with the values + /// + /// The value to format in the entity body. + /// The HTTP status code of the response. + /// The Content-Type header for the response + public ContentHttpResult(string? content, string? contentType, int? statusCode) { + Content = content; + StatusCode = statusCode; + ContentType = contentType; } /// @@ -39,39 +50,6 @@ internal ContentHttpResult() /// /// The for the current request. /// A task that represents the asynchronous execute operation. - public async Task ExecuteAsync(HttpContext httpContext) - { - var response = httpContext.Response; - ResponseContentTypeHelper.ResolveContentTypeAndEncoding( - ContentType, - response.ContentType, - (DefaultContentType, DefaultEncoding), - ResponseContentTypeHelper.GetEncoding, - out var resolvedContentType, - out var resolvedContentTypeEncoding); - - response.ContentType = resolvedContentType; - - if (StatusCode != null) - { - response.StatusCode = StatusCode.Value; - } - - var logger = httpContext.RequestServices.GetRequiredService>(); - Log.ContentResultExecuting(logger, resolvedContentType); - - if (Content != null) - { - response.ContentLength = resolvedContentTypeEncoding.GetByteCount(Content); - await response.WriteAsync(Content, resolvedContentTypeEncoding); - } - } - - private static partial class Log - { - [LoggerMessage(1, LogLevel.Information, - "Executing ContentResult with HTTP Response ContentType of {ContentType}", - EventName = "ContentResultExecuting")] - internal static partial void ContentResultExecuting(ILogger logger, string contentType); - } + public Task ExecuteAsync(HttpContext httpContext) + => HttpResultsWriter.WriteResultAsContentAsync(httpContext, Content, StatusCode, ContentType); } diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index fae86a488170..1467de8ce908 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Http; /// with status code Created (201) and Location header. /// Targets a registered route. /// -public sealed class CreatedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRouteHttpResult, IStatusCodeHttpResult +public sealed class CreatedAtRouteHttpResult : IResult { /// /// Initializes a new instance of the class with the values @@ -19,7 +19,7 @@ public sealed class CreatedAtRouteHttpResult : IResult, IObjectHttpResult, IAtRo /// /// The route data to use for generating the URL. /// The value to format in the entity body. - internal CreatedAtRouteHttpResult(object? routeValues, object? value) + public CreatedAtRouteHttpResult(object? routeValues, object? value) : this(routeName: null, routeValues: routeValues, value: value) { } @@ -31,7 +31,7 @@ internal CreatedAtRouteHttpResult(object? routeValues, object? value) /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - internal CreatedAtRouteHttpResult( + public CreatedAtRouteHttpResult( string? routeName, object? routeValues, object? value) @@ -51,13 +51,14 @@ internal CreatedAtRouteHttpResult( public RouteValueDictionary? RouteValues { get; } /// - public int? StatusCode => StatusCodes.Status201Created; + public int StatusCode => StatusCodes.Status201Created; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson( + => HttpResultsWriter.WriteResultAsJsonAsync( httpContext, - objectHttpResult: this, + Value, + StatusCode, configureResponseHeader: ConfigureResponseHeaders); private void ConfigureResponseHeaders(HttpContext context) diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index c1f2deb3f0ff..f6c1f7b8e54a 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write an object to the response /// with status code Created (201) and Location header. /// -public sealed class CreatedHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult, IAtLocationHttpResult +public sealed class CreatedHttpResult : IResult { /// /// Initializes a new instance of the class with the values @@ -15,7 +15,7 @@ public sealed class CreatedHttpResult : IResult, IObjectHttpResult, IStatusCodeH /// /// The location at which the content has been created. /// The value to format in the entity body. - internal CreatedHttpResult(string location, object? value) + public CreatedHttpResult(string location, object? value) { Value = value; Location = location; @@ -27,7 +27,7 @@ internal CreatedHttpResult(string location, object? value) /// /// The location at which the content has been created. /// The value to format in the entity body. - internal CreatedHttpResult(Uri locationUri, object? value) + public CreatedHttpResult(Uri locationUri, object? value) { Value = value; @@ -50,23 +50,24 @@ internal CreatedHttpResult(Uri locationUri, object? value) public object? Value { get; } /// - public int? StatusCode => StatusCodes.Status201Created; + public int StatusCode => StatusCodes.Status201Created; /// public string? Location { get; } /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson( - httpContext, - objectHttpResult: this, - configureResponseHeader: ConfigureResponseHeaders); - - private void ConfigureResponseHeaders(HttpContext context) { - if (!string.IsNullOrEmpty(Location)) - { - context.Response.Headers.Location = Location; - } + return HttpResultsWriter.WriteResultAsJsonAsync( + httpContext, + Value, + StatusCode, + configureResponseHeader: (context) => + { + if (!string.IsNullOrEmpty(Location)) + { + context.Response.Headers.Location = Location; + } + }); } } diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index 77419aaf8aa8..409243b51a46 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Http; /// Represents an that when executed will /// write a file from the content to the response. /// -public sealed partial class FileContentHttpResult : IResult, IFileHttpResult +public sealed partial class FileContentHttpResult : IResult { /// /// Creates a new instance with @@ -18,12 +18,52 @@ public sealed partial class FileContentHttpResult : IResult, IFileHttpResult /// provided . /// /// The bytes that represent the file contents. + /// The Content-Type of the file. + public FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) + : this(fileContents, contentType, fileDownloadName: null) + { + } + + /// + /// Creates a new instance with + /// the provided , the provided + /// and the provided . + /// + /// The bytes that represent the file contents. /// The Content-Type header of the response. - internal FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) + /// The suggested file name. + public FileContentHttpResult( + ReadOnlyMemory fileContents, + string? contentType, + string? fileDownloadName) + : this(fileContents, contentType, fileDownloadName, enableRangeProcessing: false) + { + } + + /// + /// Creates a new instance with the provided values. + /// + /// The bytes that represent the file contents. + /// The Content-Type of the file. + /// The suggested file name. + /// Set to true to enable range requests processing. + /// The of when the file was last modified. + /// The associated with the file. + public FileContentHttpResult( + ReadOnlyMemory fileContents, + string? contentType, + string? fileDownloadName, + bool enableRangeProcessing, + DateTimeOffset? lastModified = null, + EntityTagHeaderValue? entityTag = null) { FileContents = fileContents; FileLength = fileContents.Length; ContentType = contentType ?? "application/octet-stream"; + FileDownloadName = fileDownloadName; + EnableRangeProcessing = enableRangeProcessing; + LastModified = lastModified; + EntityTag = entityTag; } /// @@ -50,8 +90,14 @@ internal FileContentHttpResult(ReadOnlyMemory fileContents, string? conten public ReadOnlyMemory FileContents { get; internal init; } /// - public Task ExecuteAsync(HttpContext httpContext) => HttpResultsWriter.WriteResultAsFileAsync( - httpContext, - fileHttpResult: this, - (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength)); + public Task ExecuteAsync(HttpContext httpContext) => + HttpResultsWriter.WriteResultAsFileAsync( + httpContext, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag, + (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength)); } diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index 88dfd47f8a57..15bf2b1cbd02 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Http; /// Represents an that when executed will /// write a file from a stream to the response. /// -public sealed class FileStreamHttpResult : IResult, IFileHttpResult +public sealed class FileStreamHttpResult : IResult { /// /// Creates a new instance with @@ -18,8 +18,44 @@ public sealed class FileStreamHttpResult : IResult, IFileHttpResult /// provided . /// /// The stream with the file. + /// The Content-Type of the file. + public FileStreamHttpResult(Stream fileStream, string? contentType) + : this(fileStream, contentType, fileDownloadName: null) + { + } + + /// + /// Creates a new instance with + /// the provided , the provided + /// and the provided . + /// + /// The stream with the file. /// The Content-Type header of the response. - internal FileStreamHttpResult(Stream fileStream, string? contentType) + /// The suggested file name. + public FileStreamHttpResult( + Stream fileStream, + string? contentType, + string? fileDownloadName) + : this(fileStream, contentType, fileDownloadName, enableRangeProcessing: false) + { + } + + /// + /// Creates a new instance with the provided values. + /// + /// The stream with the file. + /// The Content-Type of the file. + /// The suggested file name. + /// Set to true to enable range requests processing. + /// The of when the file was last modified. + /// The associated with the file. + public FileStreamHttpResult( + Stream fileStream, + string? contentType, + string? fileDownloadName, + bool enableRangeProcessing, + DateTimeOffset? lastModified = null, + EntityTagHeaderValue? entityTag = null) { if (fileStream == null) { @@ -31,7 +67,12 @@ internal FileStreamHttpResult(Stream fileStream, string? contentType) { FileLength = fileStream.Length; } + ContentType = contentType ?? "application/octet-stream"; + FileDownloadName = fileDownloadName; + EnableRangeProcessing = enableRangeProcessing; + LastModified = lastModified; + EntityTag = entityTag; } /// @@ -64,7 +105,12 @@ public async Task ExecuteAsync(HttpContext httpContext) { await HttpResultsWriter.WriteResultAsFileAsync( httpContext, - fileHttpResult: this, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag, (context, range, rangeLength) => FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength)); } } diff --git a/src/Http/Http.Results/src/ForbidHttpResult.cs b/src/Http/Http.Results/src/ForbidHttpResult.cs index 1f28664a5209..3ab413dc7729 100644 --- a/src/Http/Http.Results/src/ForbidHttpResult.cs +++ b/src/Http/Http.Results/src/ForbidHttpResult.cs @@ -16,7 +16,7 @@ public sealed partial class ForbidHttpResult : IResult /// /// Initializes a new instance of . /// - internal ForbidHttpResult() + public ForbidHttpResult() : this(Array.Empty()) { } @@ -26,7 +26,7 @@ internal ForbidHttpResult() /// specified authentication scheme. /// /// The authentication scheme to challenge. - internal ForbidHttpResult(string authenticationScheme) + public ForbidHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -36,7 +36,7 @@ internal ForbidHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to challenge. - internal ForbidHttpResult(IList authenticationSchemes) + public ForbidHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -47,7 +47,7 @@ internal ForbidHttpResult(IList authenticationSchemes) /// /// used to perform the authentication /// challenge. - internal ForbidHttpResult(AuthenticationProperties? properties) + public ForbidHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } @@ -59,7 +59,7 @@ internal ForbidHttpResult(AuthenticationProperties? properties) /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - internal ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) + public ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -71,7 +71,7 @@ internal ForbidHttpResult(string authenticationScheme, AuthenticationProperties? /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - internal ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + public ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; diff --git a/src/Http/Http.Results/src/HttpResultsWriter.cs b/src/Http/Http.Results/src/HttpResultsWriter.cs index b66ddd865c7b..e97d5a8f4075 100644 --- a/src/Http/Http.Results/src/HttpResultsWriter.cs +++ b/src/Http/Http.Results/src/HttpResultsWriter.cs @@ -3,6 +3,7 @@ namespace Microsoft.AspNetCore.Http; +using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Internal; @@ -13,29 +14,20 @@ namespace Microsoft.AspNetCore.Http; internal static partial class HttpResultsWriter { - public static void WriteResultAsStatusCode( - HttpContext httpContext, - IStatusCodeHttpResult statusCodeHttpResult) - { - if (statusCodeHttpResult is { StatusCode: { } statusCode }) - { - Log.WritingResultAsStatusCode(GetLogger(httpContext), statusCodeResultType: statusCodeHttpResult.GetType().Name, statusCode); - httpContext.Response.StatusCode = statusCode; - } - } + private const string DefaultContentType = "text/plain; charset=utf-8"; + private static readonly Encoding DefaultEncoding = Encoding.UTF8; - public static Task WriteResultAsJson( + public static Task WriteResultAsJsonAsync( HttpContext httpContext, - IObjectHttpResult objectHttpResult, + object? value, + int? statusCode, string? contentType = null, JsonSerializerOptions? jsonSerializerOptions = null, Action? configureResponseHeader = null) { - Log.WritingResultAsJson(GetLogger(httpContext), objectHttpResult); - - var statusCode = objectHttpResult.StatusCode; + Log.WritingResultAsJson(GetLogger(httpContext), value, statusCode); - if (objectHttpResult.Value is ProblemDetails problemDetails) + if (value is ProblemDetails problemDetails) { ApplyProblemDetailsDefaults(problemDetails, statusCode); statusCode ??= problemDetails.Status; @@ -48,43 +40,80 @@ public static Task WriteResultAsJson( configureResponseHeader?.Invoke(httpContext); - if (objectHttpResult.Value is null) + if (value is null) { return Task.CompletedTask; } return httpContext.Response.WriteAsJsonAsync( - objectHttpResult.Value, - objectHttpResult.Value.GetType(), + value, + value.GetType(), options: jsonSerializerOptions, contentType: contentType); } + public static async Task WriteResultAsContentAsync( + HttpContext httpContext, + string? content, + int? statusCode, + string? contentType = null) + { + var response = httpContext.Response; + ResponseContentTypeHelper.ResolveContentTypeAndEncoding( + contentType, + response.ContentType, + (DefaultContentType, DefaultEncoding), + ResponseContentTypeHelper.GetEncoding, + out var resolvedContentType, + out var resolvedContentTypeEncoding); + + response.ContentType = resolvedContentType; + + if (statusCode is { } code) + { + response.StatusCode = code; + } + + Log.WritingResultAsContent(GetLogger(httpContext), resolvedContentType); + + if (content != null) + { + response.ContentLength = resolvedContentTypeEncoding.GetByteCount(content); + await response.WriteAsync(content, resolvedContentTypeEncoding); + } + } + public static Task WriteResultAsFileAsync( HttpContext httpContext, - IFileHttpResult fileHttpResult, + string? fileDownloadName, + long? fileLength, + string contentType, + bool enableRangeProcessing, + DateTimeOffset? lastModified, + EntityTagHeaderValue? entityTag, Func writeOperation) { var logger = GetLogger(httpContext); + fileDownloadName ??= string.Empty; - Log.WritingResultAsFile(logger, fileHttpResult); + Log.WritingResultAsFile(logger, fileDownloadName); var fileResultInfo = new FileResultInfo { - ContentType = fileHttpResult.ContentType, - EnableRangeProcessing = fileHttpResult.EnableRangeProcessing, - EntityTag = fileHttpResult.EntityTag, - FileDownloadName = fileHttpResult.FileDownloadName ?? string.Empty, - LastModified = fileHttpResult.LastModified, + ContentType = contentType, + EnableRangeProcessing = enableRangeProcessing, + EntityTag = entityTag, + FileDownloadName = fileDownloadName, + LastModified = lastModified, }; var (range, rangeLength, serveBody) = FileResultHelper.SetHeadersAndLog( httpContext, fileResultInfo, - fileHttpResult.FileLength, - fileHttpResult.EnableRangeProcessing, - fileHttpResult.LastModified, - fileHttpResult.EntityTag, + fileLength, + enableRangeProcessing, + lastModified, + entityTag, logger); if (!serveBody) @@ -138,49 +167,55 @@ private static ILogger GetLogger(HttpContext httpContext) return logger; } - private static partial class Log + internal static partial class Log { - public static void WritingResultAsJson(ILogger logger, IObjectHttpResult objectHttpResult) + public static void WritingResultAsJson(ILogger logger, object? value, int? statusCode) { if (logger.IsEnabled(LogLevel.Information)) { - var objectResultType = objectHttpResult.GetType().Name; - - if (objectHttpResult.Value is null) + if (value is null) { - WritingResultAsJsonWithoutValue(logger, objectResultType, objectHttpResult.StatusCode ?? StatusCodes.Status200OK); + WritingResultAsJsonWithoutValue(logger, statusCode ?? StatusCodes.Status200OK); } else { - var valueType = objectHttpResult.Value.GetType().FullName!; - WritingResultAsJson(logger, objectResultType, valueType, objectHttpResult.StatusCode ?? StatusCodes.Status200OK); + var valueType = value.GetType().FullName!; + WritingResultAsJson(logger, type: valueType, statusCode ?? StatusCodes.Status200OK); } } } - public static void WritingResultAsFile(ILogger logger, IFileHttpResult fileHttpResult) + public static void WritingResultAsFile(ILogger logger, string fileDownloadName) { if (logger.IsEnabled(LogLevel.Information)) { - var fileResultType = fileHttpResult.GetType().Name; - WritingResultAsFileWithNoFileName(logger, fileResultType, fileDownloadName: fileHttpResult.FileDownloadName ?? string.Empty); + WritingResultAsFileWithNoFileName(logger, fileDownloadName: fileDownloadName); } } [LoggerMessage(1, LogLevel.Information, - "Executing '{StatusCodeResultType}', setting HTTP status code {StatusCode}.", - EventName = "StatusCodeResultExecuting")] - public static partial void WritingResultAsStatusCode(ILogger logger, string statusCodeResultType, int statusCode); + "Setting HTTP status code {StatusCode}.", + EventName = "WritingResultAsStatusCode")] + public static partial void WritingResultAsStatusCode(ILogger logger, int statusCode); - [LoggerMessage(2, LogLevel.Information, "Executing '{ObjectResultType}', writing value of type '{Type}' with status code '{StatusCode}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)] - private static partial void WritingResultAsJson(ILogger logger, string objectResultType, string type, int statusCode); + [LoggerMessage(2, LogLevel.Information, + "Write content with HTTP Response ContentType of {ContentType}", + EventName = "WritingResultAsContent")] + public static partial void WritingResultAsContent(ILogger logger, string contentType); - [LoggerMessage(3, LogLevel.Information, "Executing result '{ObjectResultType}' with status code '{StatusCode}'.", EventName = "ObjectResultExecutingWithoutValue", SkipEnabledCheck = true)] - private static partial void WritingResultAsJsonWithoutValue(ILogger logger, string objectResultType, int statusCode); + [LoggerMessage(3, LogLevel.Information, "Writing value of type '{Type}'as Json with status code '{StatusCode}'.", + EventName = "WritingResultAsJson", + SkipEnabledCheck = true)] + private static partial void WritingResultAsJson(ILogger logger, string type, int statusCode); + + [LoggerMessage(4, LogLevel.Information, "Setting the status code '{StatusCode}' without value.", + EventName = "WritingResultAsJsonWithoutValue", + SkipEnabledCheck = true)] + private static partial void WritingResultAsJsonWithoutValue(ILogger logger, int statusCode); - [LoggerMessage(4, LogLevel.Information, - "Executing {FileResultType}, sending file with download name '{FileDownloadName}'.", - EventName = "ExecutingFileResultWithNoFileName", + [LoggerMessage(5, LogLevel.Information, + "Sending file with download name '{FileDownloadName}'.", + EventName = "WritingResultAsFileWithNoFileName", SkipEnabledCheck = true)] - private static partial void WritingResultAsFileWithNoFileName(ILogger logger, string fileResultType, string fileDownloadName); + private static partial void WritingResultAsFileWithNoFileName(ILogger logger, string fileDownloadName); } } diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index 9f54dc60dfbe..412fb72ce031 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -8,14 +8,25 @@ namespace Microsoft.AspNetCore.Http; /// /// An action result which formats the given object as JSON. /// -public sealed class JsonHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +public sealed class JsonHttpResult : IResult { - internal JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. + /// The serializer settings. + public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) : this(value, statusCode: null, jsonSerializerOptions: jsonSerializerOptions) { } - internal JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. + /// The HTTP status code of the response. + /// The serializer settings. + public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) { Value = value; StatusCode = statusCode; @@ -24,12 +35,6 @@ internal JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? j /// /// Gets or sets the serializer settings. - /// - /// When using System.Text.Json, this should be an instance of - /// - /// - /// When using Newtonsoft.Json, this should be an instance of JsonSerializerSettings. - /// /// public JsonSerializerOptions? JsonSerializerOptions { get; internal init; } @@ -46,9 +51,10 @@ internal JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? j /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson( + => HttpResultsWriter.WriteResultAsJsonAsync( httpContext, - objectHttpResult: this, + Value, + StatusCode, ContentType, JsonSerializerOptions); } diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index a6bf9f7ceceb..bc10bc951b10 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -3,23 +3,33 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// Represents an that when executed will /// produce an HTTP response with the No Content (204) status code. /// -public class NoContentHttpResult : IResult, IStatusCodeHttpResult +public class NoContentHttpResult : IResult { - internal NoContentHttpResult() + /// + /// Initializes a new instance of the class. + /// + public NoContentHttpResult() { } /// - public int? StatusCode => StatusCodes.Status204NoContent; + public int StatusCode => StatusCodes.Status204NoContent; /// public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.WriteResultAsStatusCode(httpContext, statusCodeHttpResult: this); + var logger = httpContext.RequestServices.GetRequiredService>(); + HttpResultsWriter.Log.WritingResultAsStatusCode(logger, StatusCode); + + httpContext.Response.StatusCode = StatusCode; + return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index ccb703b7dad3..9d88349acb82 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -7,8 +7,12 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write an object to the response /// with Not Found (404) status code. /// -public sealed class NotFoundObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +public sealed class NotFoundObjectHttpResult : IResult { + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. internal NotFoundObjectHttpResult(object? value) { Value = value; @@ -18,9 +22,9 @@ internal NotFoundObjectHttpResult(object? value) public object? Value { get; internal init; } /// - public int? StatusCode => StatusCodes.Status404NotFound; + public int StatusCode => StatusCodes.Status404NotFound; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index e7b584a99197..6389531a13ed 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -8,22 +8,31 @@ namespace Microsoft.AspNetCore.Http; /// /// An that on execution will write an object to the response. /// -internal sealed class ObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +internal sealed class ObjectHttpResult : IResult { /// /// Creates a new instance /// with the provided . /// - internal ObjectHttpResult(object? value) + public ObjectHttpResult(object? value) : this(value, null) { } /// /// Creates a new instance with the provided - /// and . + /// , . /// - internal ObjectHttpResult(object? value, int? statusCode) + public ObjectHttpResult(object? value, int? statusCode) + : this(value, statusCode, contentType: null) + { + } + + /// + /// Creates a new instance with the provided + /// , and . + /// + public ObjectHttpResult(object? value, int? statusCode, string? contentType) { Value = value; @@ -34,6 +43,7 @@ internal ObjectHttpResult(object? value, int? statusCode) } StatusCode = statusCode; + ContentType = contentType; } /// @@ -47,11 +57,7 @@ internal ObjectHttpResult(object? value, int? statusCode) /// public int? StatusCode { get; internal init; } - /// - /// - /// - /// - /// + /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this, ContentType); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode, ContentType); } diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index 13cd2f2edcb9..011f5b40e9e2 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -9,9 +9,13 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write an object to the response /// with Ok (200) status code. /// -public sealed class OkObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +public sealed class OkObjectHttpResult : IResult { - internal OkObjectHttpResult(object? value) + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. + public OkObjectHttpResult(object? value) { Value = value; } @@ -20,9 +24,9 @@ internal OkObjectHttpResult(object? value) public object? Value { get; internal init; } /// - public int? StatusCode => StatusCodes.Status200OK; + public int StatusCode => StatusCodes.Status200OK; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index e8314e6c003b..be7a25f0182e 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Http; /// A on execution will write a file from disk to the response /// using mechanisms provided by the host. /// -public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult +public sealed partial class PhysicalFileHttpResult : IResult { /// /// Creates a new instance with @@ -17,10 +17,50 @@ public sealed partial class PhysicalFileHttpResult : IResult, IFileHttpResult /// /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. - internal PhysicalFileHttpResult(string fileName, string? contentType) + public PhysicalFileHttpResult(string fileName, string? contentType) + : this(fileName, contentType, fileDownloadName: null) + { + } + + /// + /// Creates a new instance with + /// the provided , the provided + /// and the provided . + /// + /// The path to the file. The path must be an absolute path. + /// The Content-Type header of the response. + /// The suggested file name. + public PhysicalFileHttpResult( + string fileName, + string? contentType, + string? fileDownloadName) + : this(fileName, contentType, fileDownloadName, enableRangeProcessing: false) + { + } + + /// + /// Creates a new instance with the provided values. + /// + /// The path to the file. The path must be an absolute path. + /// The Content-Type header of the response. + /// The suggested file name. + /// Set to true to enable range requests processing. + /// The of when the file was last modified. + /// The associated with the file. + public PhysicalFileHttpResult( + string fileName, + string? contentType, + string? fileDownloadName, + bool enableRangeProcessing, + DateTimeOffset? lastModified = null, + EntityTagHeaderValue? entityTag = null) { FileName = fileName; ContentType = contentType ?? "application/octet-stream"; + FileDownloadName = fileDownloadName; + EnableRangeProcessing = enableRangeProcessing; + LastModified = lastModified; + EntityTag = entityTag; } /// @@ -64,7 +104,12 @@ public Task ExecuteAsync(HttpContext httpContext) return HttpResultsWriter.WriteResultAsFileAsync( httpContext, - fileHttpResult: this, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag, writeOperation: ExecuteCoreAsync); } diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 71de00e980ee..eb622f9139e3 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -9,9 +9,14 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write Problem Details /// HTTP API responses based on https://tools.ietf.org/html/rfc7807 /// -public sealed class ProblemHttpResult : IResult, IObjectHttpResult +public sealed class ProblemHttpResult : IResult { - internal ProblemHttpResult(ProblemDetails problemDetails) + /// + /// Creates a new instance with + /// the provided . + /// + /// The instance to format in the entity body. + public ProblemHttpResult(ProblemDetails problemDetails) { ProblemDetails = problemDetails; } @@ -26,16 +31,14 @@ internal ProblemHttpResult(ProblemDetails problemDetails) /// public string ContentType => "application/problem+json"; - /// - public object? Value => ProblemDetails; - /// public int? StatusCode => ProblemDetails.Status; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson( + => HttpResultsWriter.WriteResultAsJsonAsync( httpContext, - objectHttpResult: this, + value: ProblemDetails, + StatusCode, ContentType); } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 9c901c3a474d..ce6efb3f0d4b 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -1,42 +1,59 @@ #nullable enable Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.AcceptedAtRouteHttpResult(object? routeValues, object? value) -> void +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.AcceptedAtRouteHttpResult(string? routeName, object? routeValues, object? value) -> void Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? -Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.AcceptedHttpResult +Microsoft.AspNetCore.Http.AcceptedHttpResult.AcceptedHttpResult(System.Uri! locationUri, object? value) -> void +Microsoft.AspNetCore.Http.AcceptedHttpResult.AcceptedHttpResult(string? location, object? value) -> void Microsoft.AspNetCore.Http.AcceptedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.AcceptedHttpResult.Location.get -> string? -Microsoft.AspNetCore.Http.AcceptedHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.AcceptedHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.AcceptedHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.BadRequestObjectHttpResult +Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.BadRequestObjectHttpResult(object? error) -> void Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.ChallengeHttpResult Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult() -> void +Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void +Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(System.Collections.Generic.IList! authenticationSchemes) -> void +Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(System.Collections.Generic.IList! authenticationSchemes, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void +Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(string! authenticationScheme) -> void +Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(string! authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? Microsoft.AspNetCore.Http.ConflictObjectHttpResult Microsoft.AspNetCore.Http.ConflictObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.ConflictObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.ConflictObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.ConflictObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.ContentHttpResult Microsoft.AspNetCore.Http.ContentHttpResult.Content.get -> string? +Microsoft.AspNetCore.Http.ContentHttpResult.ContentHttpResult(string? content, string? contentType) -> void +Microsoft.AspNetCore.Http.ContentHttpResult.ContentHttpResult(string? content, string? contentType, int? statusCode) -> void Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.get -> string? Microsoft.AspNetCore.Http.ContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ContentHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.CreatedAtRouteHttpResult(object? routeValues, object? value) -> void +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.CreatedAtRouteHttpResult(string? routeName, object? routeValues, object? value) -> void Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? -Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.CreatedHttpResult +Microsoft.AspNetCore.Http.CreatedHttpResult.CreatedHttpResult(System.Uri! locationUri, object? value) -> void +Microsoft.AspNetCore.Http.CreatedHttpResult.CreatedHttpResult(string! location, object? value) -> void Microsoft.AspNetCore.Http.CreatedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.CreatedHttpResult.Location.get -> string? -Microsoft.AspNetCore.Http.CreatedHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.CreatedHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.CreatedHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.EmptyHttpResult Microsoft.AspNetCore.Http.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! @@ -45,6 +62,9 @@ Microsoft.AspNetCore.Http.FileContentHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.FileContentHttpResult.EnableRangeProcessing.get -> bool Microsoft.AspNetCore.Http.FileContentHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? Microsoft.AspNetCore.Http.FileContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.FileContentHttpResult.FileContentHttpResult(System.ReadOnlyMemory fileContents, string? contentType) -> void +Microsoft.AspNetCore.Http.FileContentHttpResult.FileContentHttpResult(System.ReadOnlyMemory fileContents, string? contentType, string? fileDownloadName) -> void +Microsoft.AspNetCore.Http.FileContentHttpResult.FileContentHttpResult(System.ReadOnlyMemory fileContents, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.FileContentHttpResult.FileContents.get -> System.ReadOnlyMemory Microsoft.AspNetCore.Http.FileContentHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.FileContentHttpResult.FileLength.get -> long? @@ -57,27 +77,40 @@ Microsoft.AspNetCore.Http.FileStreamHttpResult.ExecuteAsync(Microsoft.AspNetCore Microsoft.AspNetCore.Http.FileStreamHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.FileStreamHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStream.get -> System.IO.Stream! +Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStreamHttpResult(System.IO.Stream! fileStream, string? contentType) -> void +Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStreamHttpResult(System.IO.Stream! fileStream, string? contentType, string? fileDownloadName) -> void +Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStreamHttpResult(System.IO.Stream! fileStream, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.FileStreamHttpResult.LastModified.get -> System.DateTimeOffset? Microsoft.AspNetCore.Http.ForbidHttpResult Microsoft.AspNetCore.Http.ForbidHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! Microsoft.AspNetCore.Http.ForbidHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult() -> void +Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void +Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(System.Collections.Generic.IList! authenticationSchemes) -> void +Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(System.Collections.Generic.IList! authenticationSchemes, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void +Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(string! authenticationScheme) -> void +Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(string! authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.ForbidHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? Microsoft.AspNetCore.Http.JsonHttpResult Microsoft.AspNetCore.Http.JsonHttpResult.ContentType.get -> string? Microsoft.AspNetCore.Http.JsonHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void +Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, int? statusCode, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void Microsoft.AspNetCore.Http.JsonHttpResult.JsonSerializerOptions.get -> System.Text.Json.JsonSerializerOptions? Microsoft.AspNetCore.Http.JsonHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.JsonHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.NoContentHttpResult Microsoft.AspNetCore.Http.NoContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.NoContentHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.NoContentHttpResult.NoContentHttpResult() -> void +Microsoft.AspNetCore.Http.NoContentHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.NotFoundObjectHttpResult Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.OkObjectHttpResult Microsoft.AspNetCore.Http.OkObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.OkObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.OkObjectHttpResult.OkObjectHttpResult(object? value) -> void +Microsoft.AspNetCore.Http.OkObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.OkObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.PhysicalFileHttpResult Microsoft.AspNetCore.Http.PhysicalFileHttpResult.ContentType.get -> string! @@ -88,12 +121,15 @@ Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileName.get -> string! Microsoft.AspNetCore.Http.PhysicalFileHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.PhysicalFileHttpResult(string! fileName, string? contentType) -> void +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.PhysicalFileHttpResult(string! fileName, string? contentType, string? fileDownloadName) -> void +Microsoft.AspNetCore.Http.PhysicalFileHttpResult.PhysicalFileHttpResult(string! fileName, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.ProblemHttpResult Microsoft.AspNetCore.Http.ProblemHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.ProblemHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemDetails.get -> Microsoft.AspNetCore.Mvc.ProblemDetails! +Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemHttpResult(Microsoft.AspNetCore.Mvc.ProblemDetails! problemDetails) -> void Microsoft.AspNetCore.Http.ProblemHttpResult.StatusCode.get -> int? -Microsoft.AspNetCore.Http.ProblemHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.PushStreamHttpResult Microsoft.AspNetCore.Http.PushStreamHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.PushStreamHttpResult.EnableRangeProcessing.get -> bool @@ -102,36 +138,61 @@ Microsoft.AspNetCore.Http.PushStreamHttpResult.ExecuteAsync(Microsoft.AspNetCore Microsoft.AspNetCore.Http.PushStreamHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.PushStreamHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.PushStreamHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.PushStreamHttpResult.PushStreamHttpResult(System.Func! streamWriterCallback, string? contentType) -> void +Microsoft.AspNetCore.Http.PushStreamHttpResult.PushStreamHttpResult(System.Func! streamWriterCallback, string? contentType, string? fileDownloadName) -> void +Microsoft.AspNetCore.Http.PushStreamHttpResult.PushStreamHttpResult(System.Func! streamWriterCallback, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.RedirectHttpResult Microsoft.AspNetCore.Http.RedirectHttpResult.AcceptLocalUrlOnly.get -> bool Microsoft.AspNetCore.Http.RedirectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.RedirectHttpResult.Permanent.get -> bool Microsoft.AspNetCore.Http.RedirectHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url) -> void +Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url, bool acceptLocalUrlOnly, bool permanent, bool preserveMethod) -> void +Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url, bool permanent) -> void +Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url, bool permanent, bool preserveMethod) -> void Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.AcceptLocalUrlOnly.get -> bool Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Fragment.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Permanent.get -> bool Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.PreserveMethod.get -> bool +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(object? routeValues) -> void +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues) -> void +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent) -> void +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent, bool preserveMethod) -> void +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent, bool preserveMethod, string? fragment) -> void +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent, string? fragment) -> void +Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, string? fragment) -> void Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Url.get -> string? Microsoft.AspNetCore.Http.SignInHttpResult Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.get -> string? Microsoft.AspNetCore.Http.SignInHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.SignInHttpResult.Principal.get -> System.Security.Claims.ClaimsPrincipal! Microsoft.AspNetCore.Http.SignInHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.SignInHttpResult.SignInHttpResult(System.Security.Claims.ClaimsPrincipal! principal) -> void +Microsoft.AspNetCore.Http.SignInHttpResult.SignInHttpResult(System.Security.Claims.ClaimsPrincipal! principal, string? authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.SignOutHttpResult Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! Microsoft.AspNetCore.Http.SignOutHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? +Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult() -> void +Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(Microsoft.AspNetCore.Authentication.AuthenticationProperties! properties) -> void +Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(System.Collections.Generic.IList! authenticationSchemes) -> void +Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(System.Collections.Generic.IList! authenticationSchemes, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void +Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(string! authenticationScheme) -> void +Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(string! authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void +Microsoft.AspNetCore.Http.StatusCodeHttpResult +Microsoft.AspNetCore.Http.StatusCodeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.UnauthorizedHttpResult Microsoft.AspNetCore.Http.UnauthorizedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.UnauthorizedHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.UnauthorizedHttpResult.StatusCode.get -> int +Microsoft.AspNetCore.Http.UnauthorizedHttpResult.UnauthorizedHttpResult() -> void Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.StatusCode.get -> int? +Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.StatusCode.get -> int +Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.UnprocessableEntityObjectHttpResult(object? error) -> void Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.VirtualFileHttpResult Microsoft.AspNetCore.Http.VirtualFileHttpResult.ContentType.get -> string! @@ -142,6 +203,9 @@ Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileName.get -> string! Microsoft.AspNetCore.Http.VirtualFileHttpResult.LastModified.get -> System.DateTimeOffset? +Microsoft.AspNetCore.Http.VirtualFileHttpResult.VirtualFileHttpResult(string! fileName, string? contentType) -> void +Microsoft.AspNetCore.Http.VirtualFileHttpResult.VirtualFileHttpResult(string! fileName, string? contentType, string? fileDownloadName) -> void +Microsoft.AspNetCore.Http.VirtualFileHttpResult.VirtualFileHttpResult(string! fileName, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, bool enableRangeProcessing = false, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index b5315fe02b65..f69cfa3307e8 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -9,14 +9,60 @@ namespace Microsoft.AspNetCore.Http; /// Represents an that when executed will /// write a file from the writer callback to the response. /// -public sealed class PushStreamHttpResult : IResult, IFileHttpResult +public sealed class PushStreamHttpResult : IResult { private readonly Func _streamWriterCallback; - internal PushStreamHttpResult(Func streamWriterCallback, string? contentType) + /// + /// Creates a new instance with + /// the provided and the provided . + /// + /// The stream writer callback. + /// The Content-Type header of the response. + public PushStreamHttpResult(Func streamWriterCallback, string? contentType) + : this(streamWriterCallback, contentType, fileDownloadName: null) + { + } + + /// + /// Creates a new instance with + /// the provided , the provided + /// and the provided . + /// + /// The stream writer callback. + /// The Content-Type header of the response. + /// The suggested file name. + public PushStreamHttpResult( + Func streamWriterCallback, + string? contentType, + string? fileDownloadName) + : this(streamWriterCallback, contentType, fileDownloadName, enableRangeProcessing: false) + { + } + + /// + /// Creates a new instance with the provided values. + /// + /// The stream writer callback. + /// The Content-Type header of the response. + /// The suggested file name. + /// Set to true to enable range requests processing. + /// The of when the file was last modified. + /// The associated with the file. + public PushStreamHttpResult( + Func streamWriterCallback, + string? contentType, + string? fileDownloadName, + bool enableRangeProcessing, + DateTimeOffset? lastModified = null, + EntityTagHeaderValue? entityTag = null) { _streamWriterCallback = streamWriterCallback; ContentType = contentType ?? "application/octet-stream"; + FileDownloadName = fileDownloadName; + EnableRangeProcessing = enableRangeProcessing; + LastModified = lastModified; + EntityTag = entityTag; } /// @@ -38,8 +84,14 @@ internal PushStreamHttpResult(Func streamWriterCallback, string? c public long? FileLength { get; internal set; } /// - public Task ExecuteAsync(HttpContext httpContext) => HttpResultsWriter.WriteResultAsFileAsync( + public Task ExecuteAsync(HttpContext httpContext) => + HttpResultsWriter.WriteResultAsFileAsync( httpContext, - fileHttpResult: this, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag, (context, _, _) => _streamWriterCallback(context.Response.Body)); } diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index 5fcfab0f80b1..9fdbe2aaaf76 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Http; /// An that returns a Found (302), Moved Permanently (301), Temporary Redirect (307), /// or Permanent Redirect (308) response with a Location header to the supplied URL. /// -public sealed partial class RedirectHttpResult : IResult, IRedirectHttpResult +public sealed partial class RedirectHttpResult : IResult { private readonly string _url; @@ -20,10 +20,8 @@ public sealed partial class RedirectHttpResult : IResult, IRedirectHttpResult /// provided. /// /// The URL to redirect to. - /// If set to true, only local URLs are accepted and - /// will throw an exception when the supplied URL is not considered local. (Default: false) - internal RedirectHttpResult(string url, bool acceptLocalUrlOnly = false) - : this(url, permanent: false, acceptLocalUrlOnly) + public RedirectHttpResult(string url) + : this(url, permanent: false) { } @@ -33,13 +31,23 @@ internal RedirectHttpResult(string url, bool acceptLocalUrlOnly = false) /// /// The URL to redirect to. /// Specifies whether the redirect should be permanent (301) or temporary (302). - /// If set to true, only local URLs are accepted - /// and will throw an exception when the supplied URL is not considered local. (Default: false) - internal RedirectHttpResult(string url, bool permanent, bool acceptLocalUrlOnly = false) - : this(url, permanent, preserveMethod: false, acceptLocalUrlOnly) + public RedirectHttpResult(string url, bool permanent) + : this(url, permanent, preserveMethod: false) { } + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The URL to redirect to. + /// Specifies whether the redirect should be permanent (301) or temporary (302). + /// If set to true, make the temporary redirect (307) + /// or permanent redirect (308) preserve the initial request method. + public RedirectHttpResult(string url, bool permanent, bool preserveMethod) + : this(url, acceptLocalUrlOnly: false, permanent, preserveMethod) + { } + /// /// Initializes a new instance of the class with the values /// provided. @@ -49,8 +57,8 @@ internal RedirectHttpResult(string url, bool permanent, bool acceptLocalUrlOnly /// If set to true, make the temporary redirect (307) /// or permanent redirect (308) preserve the initial request method. /// If set to true, only local URLs are accepted - /// and will throw an exception when the supplied URL is not considered local. (Default: false) - internal RedirectHttpResult(string url, bool permanent, bool preserveMethod, bool acceptLocalUrlOnly = false) + /// and will throw an exception when the supplied URL is not considered local. + public RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, bool preserveMethod) { if (url == null) { diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index 89bf304c6bc3..7d146a1a2034 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -12,16 +12,14 @@ namespace Microsoft.AspNetCore.Http; /// or Permanent Redirect (308) response with a Location header. /// Targets a registered route. /// -public sealed partial class RedirectToRouteHttpResult : IResult, IRedirectHttpResult, IAtRouteHttpResult +public sealed partial class RedirectToRouteHttpResult : IResult { - private string? _destinationUrl; - /// /// Initializes a new instance of the with the values /// provided. /// /// The parameters for the route. - internal RedirectToRouteHttpResult(object? routeValues) + public RedirectToRouteHttpResult(object? routeValues) : this(routeName: null, routeValues: routeValues) { } @@ -32,7 +30,7 @@ internal RedirectToRouteHttpResult(object? routeValues) /// /// The name of the route. /// The parameters for the route. - internal RedirectToRouteHttpResult( + public RedirectToRouteHttpResult( string? routeName, object? routeValues) : this(routeName, routeValues, permanent: false) @@ -47,7 +45,7 @@ internal RedirectToRouteHttpResult( /// The parameters for the route. /// If set to true, makes the redirect permanent (301). /// Otherwise a temporary redirect is used (302). - internal RedirectToRouteHttpResult( + public RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent) @@ -65,7 +63,7 @@ internal RedirectToRouteHttpResult( /// Otherwise a temporary redirect is used (302). /// If set to true, make the temporary redirect (307) /// or permanent redirect (308) preserve the initial request method. - internal RedirectToRouteHttpResult( + public RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -81,7 +79,7 @@ internal RedirectToRouteHttpResult( /// The name of the route. /// The parameters for the route. /// The fragment to add to the URL. - internal RedirectToRouteHttpResult( + public RedirectToRouteHttpResult( string? routeName, object? routeValues, string? fragment) @@ -98,7 +96,7 @@ internal RedirectToRouteHttpResult( /// If set to true, makes the redirect permanent (301). /// Otherwise a temporary redirect is used (302). /// The fragment to add to the URL. - internal RedirectToRouteHttpResult( + public RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -118,7 +116,7 @@ internal RedirectToRouteHttpResult( /// If set to true, make the temporary redirect (307) /// or permanent redirect (308) preserve the initial request method. /// The fragment to add to the URL. - internal RedirectToRouteHttpResult( + public RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -147,40 +145,34 @@ internal RedirectToRouteHttpResult( /// public string? Fragment { get; } - /// - public bool AcceptLocalUrlOnly => true; - - /// - public string? Url => _destinationUrl; - /// public Task ExecuteAsync(HttpContext httpContext) { var linkGenerator = httpContext.RequestServices.GetRequiredService(); - _destinationUrl = linkGenerator.GetUriByRouteValues( + var destinationUrl = linkGenerator.GetUriByRouteValues( httpContext, RouteName, RouteValues, fragment: Fragment == null ? FragmentString.Empty : new FragmentString("#" + Fragment)); - if (string.IsNullOrEmpty(_destinationUrl)) + if (string.IsNullOrEmpty(destinationUrl)) { throw new InvalidOperationException("No route matches the supplied values."); } var logger = httpContext.RequestServices.GetRequiredService>(); - Log.RedirectToRouteResultExecuting(logger, _destinationUrl, RouteName); + Log.RedirectToRouteResultExecuting(logger, destinationUrl, RouteName); if (PreserveMethod) { httpContext.Response.StatusCode = Permanent ? StatusCodes.Status308PermanentRedirect : StatusCodes.Status307TemporaryRedirect; - httpContext.Response.Headers.Location = _destinationUrl; + httpContext.Response.Headers.Location = destinationUrl; } else { - httpContext.Response.Redirect(_destinationUrl, Permanent); + httpContext.Response.Redirect(destinationUrl, Permanent); } return Task.CompletedTask; diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 84f5920673df..89b9c12bde7d 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -63,7 +63,7 @@ public static IResult SignIn( ClaimsPrincipal principal, AuthenticationProperties? properties = null, string? authenticationScheme = null) - => new SignInHttpResult(authenticationScheme, principal, properties); + => new SignInHttpResult(principal, authenticationScheme, properties); /// /// Creates an that on execution invokes . @@ -114,11 +114,7 @@ public static IResult Text(string content, string? contentType = null, Encoding? mediaTypeHeaderValue.Encoding = contentEncoding ?? mediaTypeHeaderValue.Encoding; } - return new ContentHttpResult - { - Content = content, - ContentType = mediaTypeHeaderValue?.ToString() - }; + return new ContentHttpResult(content, mediaTypeHeaderValue?.ToString()); } /// @@ -128,11 +124,7 @@ public static IResult Text(string content, string? contentType = null, Encoding? /// The content type (MIME type). /// The created object for the response. public static IResult Content(string content, MediaTypeHeaderValue contentType) - => new ContentHttpResult - { - Content = content, - ContentType = contentType.ToString() - }; + => new ContentHttpResult(content, contentType.ToString()); /// /// Creates a that serializes the specified object to JSON. @@ -161,11 +153,11 @@ public static IResult Json(object? data, JsonSerializerOptions? options = null, /// This API is an alias for . /// /// The file contents. - /// The Content-Type of the file. - /// The suggested file name. - /// Set to true to enable range requests processing. - /// The of when the file was last modified. - /// The associated with the file. + /// The Content-Type of the file. + /// The suggested file name. + /// Set to true to enable range requests processing. + /// The of when the file was last modified. + /// The associated with the file. /// The created for the response. #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult File( @@ -472,7 +464,7 @@ public static IResult Redirect(string url, bool permanent = false, bool preserve /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the initial request method. /// The created for the response. public static IResult LocalRedirect(string localUrl, bool permanent = false, bool preserveMethod = false) - => new RedirectHttpResult(localUrl, permanent, preserveMethod, acceptLocalUrlOnly: true); + => new RedirectHttpResult(localUrl, acceptLocalUrlOnly: true, permanent, preserveMethod); /// /// Redirects to the specified route. diff --git a/src/Http/Http.Results/src/SignInHttpResult.cs b/src/Http/Http.Results/src/SignInHttpResult.cs index 7b21b4b0fb46..d96b54c42220 100644 --- a/src/Http/Http.Results/src/SignInHttpResult.cs +++ b/src/Http/Http.Results/src/SignInHttpResult.cs @@ -18,30 +18,8 @@ public sealed partial class SignInHttpResult : IResult /// default authentication scheme. /// /// The claims principal containing the user claims. - internal SignInHttpResult(ClaimsPrincipal principal) - : this(authenticationScheme: null, principal, properties: null) - { - } - - /// - /// Initializes a new instance of with the - /// specified authentication scheme. - /// - /// The authentication scheme to use when signing in the user. - /// The claims principal containing the user claims. - internal SignInHttpResult(string? authenticationScheme, ClaimsPrincipal principal) - : this(authenticationScheme, principal, properties: null) - { - } - - /// - /// Initializes a new instance of with the - /// default authentication scheme and . - /// - /// The claims principal containing the user claims. - /// used to perform the sign-in operation. - internal SignInHttpResult(ClaimsPrincipal principal, AuthenticationProperties? properties) - : this(authenticationScheme: null, principal, properties) + public SignInHttpResult(ClaimsPrincipal principal) + : this(principal, authenticationScheme: null, properties: null) { } @@ -49,10 +27,10 @@ internal SignInHttpResult(ClaimsPrincipal principal, AuthenticationProperties? p /// Initializes a new instance of with the /// specified authentication scheme and . /// - /// The authentication schemes to use when signing in the user. /// The claims principal containing the user claims. + /// The authentication schemes to use when signing in the user. /// used to perform the sign-in operation. - internal SignInHttpResult(string? authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties? properties) + public SignInHttpResult(ClaimsPrincipal principal, string? authenticationScheme, AuthenticationProperties? properties) { Principal = principal ?? throw new ArgumentNullException(nameof(principal)); AuthenticationScheme = authenticationScheme; diff --git a/src/Http/Http.Results/src/SignOutHttpResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs index 5bd79b39beb1..8b48fe72b290 100644 --- a/src/Http/Http.Results/src/SignOutHttpResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -16,7 +16,7 @@ public sealed partial class SignOutHttpResult : IResult /// /// Initializes a new instance of with the default sign out scheme. /// - internal SignOutHttpResult() + public SignOutHttpResult() : this(Array.Empty()) { } @@ -26,7 +26,7 @@ internal SignOutHttpResult() /// specified authentication scheme and . /// /// used to perform the sign-out operation. - internal SignOutHttpResult(AuthenticationProperties properties) + public SignOutHttpResult(AuthenticationProperties properties) : this(Array.Empty(), properties) { } @@ -36,7 +36,7 @@ internal SignOutHttpResult(AuthenticationProperties properties) /// specified authentication scheme. /// /// The authentication scheme to use when signing out the user. - internal SignOutHttpResult(string authenticationScheme) + public SignOutHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -46,7 +46,7 @@ internal SignOutHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to use when signing out the user. - internal SignOutHttpResult(IList authenticationSchemes) + public SignOutHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -57,7 +57,7 @@ internal SignOutHttpResult(IList authenticationSchemes) /// /// The authentication schemes to use when signing out the user. /// used to perform the sign-out operation. - internal SignOutHttpResult(string authenticationScheme, AuthenticationProperties? properties) + public SignOutHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -68,7 +68,7 @@ internal SignOutHttpResult(string authenticationScheme, AuthenticationProperties /// /// The authentication scheme to use when signing out the user. /// used to perform the sign-out operation. - internal SignOutHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + public SignOutHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { if (authenticationSchemes is null) { diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index dfadbf9356b5..31e4f2006e5f 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -3,11 +3,14 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// Represents an that when executed will /// produce an HTTP response with the given response status code. /// -internal sealed partial class StatusCodeHttpResult : IResult, IStatusCodeHttpResult +public sealed partial class StatusCodeHttpResult : IResult { /// /// Initializes a new instance of the class @@ -20,7 +23,7 @@ internal StatusCodeHttpResult(int statusCode) } /// - public int? StatusCode { get; } + public int StatusCode { get; } /// /// Sets the status code on the HTTP response. @@ -29,7 +32,11 @@ internal StatusCodeHttpResult(int statusCode) /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.WriteResultAsStatusCode(httpContext, statusCodeHttpResult: this); + var logger = httpContext.RequestServices.GetRequiredService>(); + HttpResultsWriter.Log.WritingResultAsStatusCode(logger, StatusCode); + + httpContext.Response.StatusCode = StatusCode; + return Task.CompletedTask; } } diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index 58f4302476de..e676e9196c6d 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -3,23 +3,33 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// Represents an that when executed will /// produce an HTTP response with the No Unauthorized (401) status code. /// -public sealed class UnauthorizedHttpResult : IResult, IStatusCodeHttpResult +public sealed class UnauthorizedHttpResult : IResult { - internal UnauthorizedHttpResult() + /// + /// Initializes a new instance of the class. + /// + public UnauthorizedHttpResult() { } /// - public int? StatusCode => StatusCodes.Status401Unauthorized; + public int StatusCode => StatusCodes.Status401Unauthorized; /// public Task ExecuteAsync(HttpContext httpContext) { - HttpResultsWriter.WriteResultAsStatusCode(httpContext, statusCodeHttpResult: this); + var logger = httpContext.RequestServices.GetRequiredService>(); + HttpResultsWriter.Log.WritingResultAsStatusCode(logger, StatusCode); + + httpContext.Response.StatusCode = StatusCode; + return Task.CompletedTask; } diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index 7b99bc3fb107..45520545bab8 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -7,9 +7,14 @@ namespace Microsoft.AspNetCore.Http; /// An that on execution will write an object to the response /// with Unprocessable Entity (422) status code. /// -public sealed class UnprocessableEntityObjectHttpResult : IResult, IObjectHttpResult, IStatusCodeHttpResult +public sealed class UnprocessableEntityObjectHttpResult : IResult { - internal UnprocessableEntityObjectHttpResult(object? error) + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The error content to format in the entity body. + public UnprocessableEntityObjectHttpResult(object? error) { Value = error; } @@ -18,9 +23,9 @@ internal UnprocessableEntityObjectHttpResult(object? error) public object? Value { get; internal init; } /// > - public int? StatusCode => StatusCodes.Status422UnprocessableEntity; + public int StatusCode => StatusCodes.Status422UnprocessableEntity; /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJson(httpContext, objectHttpResult: this); + => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index 26ba34976f80..f4aeb073c3ee 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -13,21 +13,61 @@ namespace Microsoft.AspNetCore.Http; /// A that on execution writes the file specified /// using a virtual path to the response using mechanisms provided by the host. /// -public sealed class VirtualFileHttpResult : IResult, IFileHttpResult +public sealed class VirtualFileHttpResult : IResult { private string _fileName; private IFileInfo? _fileInfo; /// - /// Creates a new instance with the provided - /// and the provided . + /// Creates a new instance with + /// the provided and the provided . /// - /// The path to the file. The path must be relative/virtual. + /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. - internal VirtualFileHttpResult(string fileName, string? contentType) + public VirtualFileHttpResult(string fileName, string? contentType) + : this(fileName, contentType, fileDownloadName: null) + { + } + + /// + /// Creates a new instance with + /// the provided , the provided + /// and the provided . + /// + /// The path to the file. The path must be an absolute path. + /// The Content-Type header of the response. + /// The suggested file name. + public VirtualFileHttpResult( + string fileName, + string? contentType, + string? fileDownloadName) + : this(fileName, contentType, fileDownloadName, enableRangeProcessing: false) + { + } + + /// + /// Creates a new instance with the provided values. + /// + /// The path to the file. The path must be an absolute path. + /// The Content-Type header of the response. + /// The suggested file name. + /// Set to true to enable range requests processing. + /// The of when the file was last modified. + /// The associated with the file. + public VirtualFileHttpResult( + string fileName, + string? contentType, + string? fileDownloadName, + bool enableRangeProcessing, + DateTimeOffset? lastModified = null, + EntityTagHeaderValue? entityTag = null) { FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); ContentType = contentType ?? "application/octet-stream"; + FileDownloadName = fileDownloadName; + EnableRangeProcessing = enableRangeProcessing; + LastModified = lastModified; + EntityTag = entityTag; } /// @@ -75,7 +115,12 @@ public Task ExecuteAsync(HttpContext httpContext) return HttpResultsWriter.WriteResultAsFileAsync( httpContext, - fileHttpResult: this, + FileDownloadName, + FileLength, + ContentType, + EnableRangeProcessing, + LastModified, + EntityTag, ExecuteCoreAsync); } diff --git a/src/Http/Http.Results/test/ContentResultTest.cs b/src/Http/Http.Results/test/ContentResultTest.cs index cb61e66e1e1c..f45ea77e6226 100644 --- a/src/Http/Http.Results/test/ContentResultTest.cs +++ b/src/Http/Http.Results/test/ContentResultTest.cs @@ -15,14 +15,12 @@ public class ContentResultTest public async Task ContentResult_ExecuteAsync_Response_NullContent_SetsContentTypeAndEncoding() { // Arrange - var contentResult = new ContentHttpResult + var contentType = new MediaTypeHeaderValue("text/plain") { - Content = null, - ContentType = new MediaTypeHeaderValue("text/plain") - { - Encoding = Encoding.Unicode - }.ToString() - }; + Encoding = Encoding.Unicode + }.ToString(); + + var contentResult = new ContentHttpResult(null, contentType); var httpContext = GetHttpContext(); // Act @@ -109,11 +107,7 @@ public async Task ContentResult_ExecuteAsync_SetContentTypeAndEncoding_OnRespons byte[] expectedContentData) { // Arrange - var contentResult = new ContentHttpResult - { - Content = content, - ContentType = contentType?.ToString() - }; + var contentResult = new ContentHttpResult(content, contentType?.ToString()); var httpContext = GetHttpContext(); var memoryStream = new MemoryStream(); httpContext.Response.Body = memoryStream; diff --git a/src/Http/Http.Results/test/LocalRedirectResultTest.cs b/src/Http/Http.Results/test/LocalRedirectResultTest.cs index 76970a4f1cf3..abd58a3cbec1 100644 --- a/src/Http/Http.Results/test/LocalRedirectResultTest.cs +++ b/src/Http/Http.Results/test/LocalRedirectResultTest.cs @@ -16,7 +16,7 @@ public void Constructor_WithParameterUrl_SetsResultUrlAndNotPermanentOrPreserveM var url = "/test/url"; // Act - var result = new RedirectHttpResult(url, acceptLocalUrlOnly: true); + var result = new RedirectHttpResult(url, acceptLocalUrlOnly: true, false, false); // Assert Assert.False(result.PreserveMethod); @@ -31,7 +31,7 @@ public void Constructor_WithParameterUrlAndPermanent_SetsResultUrlAndPermanentNo var url = "/test/url"; // Act - var result = new RedirectHttpResult(url, permanent: true, acceptLocalUrlOnly: true); + var result = new RedirectHttpResult(url, acceptLocalUrlOnly: true, permanent: true, preserveMethod: false); // Assert Assert.False(result.PreserveMethod); @@ -46,7 +46,7 @@ public void Constructor_WithParameterUrlAndPermanent_SetsResultUrlPermanentAndPr var url = "/test/url"; // Act - var result = new RedirectHttpResult(url, permanent: true, preserveMethod: true, acceptLocalUrlOnly: true); + var result = new RedirectHttpResult(url, acceptLocalUrlOnly: true, permanent: true, preserveMethod: true); // Assert Assert.True(result.PreserveMethod); @@ -63,7 +63,7 @@ public async Task Execute_ReturnsExpectedValues() var expectedPath = "/Home/About"; var httpContext = GetHttpContext(appRoot); - var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true); + var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true, false, false); // Act await result.ExecuteAsync(httpContext); @@ -86,7 +86,7 @@ public async Task Execute_Throws_ForNonLocalUrl( { // Arrange var httpContext = GetHttpContext(appRoot); - var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true); + var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true, false, false); // Act & Assert var exception = await Assert.ThrowsAsync(() => result.ExecuteAsync(httpContext)); @@ -107,7 +107,7 @@ public async Task Execute_Throws_ForNonLocalUrlTilde( { // Arrange var httpContext = GetHttpContext(appRoot); - var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true); + var result = new RedirectHttpResult(contentPath, acceptLocalUrlOnly: true, false, false); // Act & Assert var exception = await Assert.ThrowsAsync(() => result.ExecuteAsync(httpContext)); diff --git a/src/Http/Http.Results/test/NoContentResultTests.cs b/src/Http/Http.Results/test/NoContentResultTests.cs new file mode 100644 index 000000000000..55cdffb5ad09 --- /dev/null +++ b/src/Http/Http.Results/test/NoContentResultTests.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Result; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +public class NoContentResultTests +{ + [Fact] + public void NoContentResultTests_InitializesStatusCode() + { + // Arrange & act + var result = new NoContentHttpResult(); + + // Assert + Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); + } + + + [Fact] + public void NoContentResultTests_ExecuteResultSetsResponseStatusCode() + { + // Arrange + var result = new NoContentHttpResult(); + + var httpContext = GetHttpContext(); + + // Act + result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status204NoContent, httpContext.Response.StatusCode); + } + + private static IServiceCollection CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(NullLoggerFactory.Instance); + services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); + return services; + } + + private static HttpContext GetHttpContext() + { + var services = CreateServices(); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = services.BuildServiceProvider(); + + return httpContext; + } +} diff --git a/src/Http/Http.Results/test/SignInResultTest.cs b/src/Http/Http.Results/test/SignInResultTest.cs index f56429d4607b..38d33edba4ee 100644 --- a/src/Http/Http.Results/test/SignInResultTest.cs +++ b/src/Http/Http.Results/test/SignInResultTest.cs @@ -24,7 +24,7 @@ public async Task ExecuteAsync_InvokesSignInAsyncOnAuthenticationManager() .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignInHttpResult("", principal, null); + var result = new SignInHttpResult(principal, "", null); // Act await result.ExecuteAsync(httpContext); @@ -65,7 +65,7 @@ public async Task ExecuteAsync_InvokesSignInAsyncOnConfiguredScheme() .Returns(Task.CompletedTask) .Verifiable(); var httpContext = GetHttpContext(auth.Object); - var result = new SignInHttpResult("Scheme1", principal, authProperties); + var result = new SignInHttpResult(principal, "Scheme1", authProperties); // Act await result.ExecuteAsync(httpContext); diff --git a/src/Http/Http.Results/test/StatusCodeResultTests.cs b/src/Http/Http.Results/test/StatusCodeResultTests.cs index ef6c0209fd5c..ae3493329307 100644 --- a/src/Http/Http.Results/test/StatusCodeResultTests.cs +++ b/src/Http/Http.Results/test/StatusCodeResultTests.cs @@ -28,6 +28,7 @@ private static IServiceCollection CreateServices() { var services = new ServiceCollection(); services.AddSingleton(NullLoggerFactory.Instance); + services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); return services; } diff --git a/src/Http/Http.Results/test/UnauthorizedResultTests.cs b/src/Http/Http.Results/test/UnauthorizedResultTests.cs index 66f09a44372c..50aab7fd7bf9 100644 --- a/src/Http/Http.Results/test/UnauthorizedResultTests.cs +++ b/src/Http/Http.Results/test/UnauthorizedResultTests.cs @@ -3,6 +3,10 @@ namespace Microsoft.AspNetCore.Http.Result; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + public class UnauthorizedResultTests { [Fact] @@ -14,4 +18,37 @@ public void UnauthorizedResult_InitializesStatusCode() // Assert Assert.Equal(StatusCodes.Status401Unauthorized, result.StatusCode); } + + [Fact] + public void UnauthorizedResult_ExecuteResultSetsResponseStatusCode() + { + // Arrange + var result = new UnauthorizedHttpResult(); + + var httpContext = GetHttpContext(); + + // Act + result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status401Unauthorized, httpContext.Response.StatusCode); + } + + private static IServiceCollection CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(NullLoggerFactory.Instance); + services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); + return services; + } + + private static HttpContext GetHttpContext() + { + var services = CreateServices(); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = services.BuildServiceProvider(); + + return httpContext; + } } From 7d89ce3a7989a6bcd82d0f3a8eee4daa3c42bc50 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 09:54:03 -0700 Subject: [PATCH 14/29] Fixing xml docs --- .../src/AcceptedAtRouteHttpResult.cs | 16 +++++++++---- .../Http.Results/src/AcceptedHttpResult.cs | 12 +++++++--- .../src/BadRequestObjectHttpResult.cs | 8 +++++-- .../src/ConflictObjectHttpResult.cs | 8 +++++-- .../Http.Results/src/ContentHttpResult.cs | 4 +++- .../src/CreatedAtRouteHttpResult.cs | 16 +++++++++---- .../Http.Results/src/CreatedHttpResult.cs | 8 +++++-- .../Http.Results/src/FileContentHttpResult.cs | 24 ++++++++++++++----- .../Http.Results/src/FileStreamHttpResult.cs | 24 ++++++++++++++----- src/Http/Http.Results/src/JsonHttpResult.cs | 8 +++++-- .../Http.Results/src/NoContentHttpResult.cs | 4 +++- .../src/NotFoundObjectHttpResult.cs | 8 +++++-- src/Http/Http.Results/src/ObjectHttpResult.cs | 8 +++++-- .../Http.Results/src/OkObjectHttpResult.cs | 8 +++++-- .../src/PhysicalFileHttpResult.cs | 24 ++++++++++++++----- .../Http.Results/src/ProblemHttpResult.cs | 4 +++- .../Http.Results/src/PushStreamHttpResult.cs | 24 ++++++++++++++----- .../Http.Results/src/RedirectHttpResult.cs | 16 +++++++++---- .../src/RedirectToRouteHttpResult.cs | 20 ++++++++++++---- .../Http.Results/src/StatusCodeHttpResult.cs | 4 +++- .../src/UnauthorizedHttpResult.cs | 4 +++- 21 files changed, 189 insertions(+), 63 deletions(-) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index 5380636e7e4e..5daf87eda2b7 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -42,16 +42,24 @@ public AcceptedAtRouteHttpResult( RouteValues = new RouteValueDictionary(routeValues); } - /// + /// + /// Gets the object result. + /// public object? Value { get; } - /// + /// + /// Gets the name of the route to use for generating the URL. + /// public string? RouteName { get; } - /// + /// + /// Gets the route data to use for generating the URL. + /// public RouteValueDictionary? RouteValues { get; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status202Accepted; /// diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index e610dd979940..cede3cc196e4 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -49,13 +49,19 @@ public AcceptedHttpResult(Uri locationUri, object? value) } } - /// + /// + /// Gets the object result. + /// public object? Value { get; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status202Accepted; - /// + /// + /// Gets the location at which the status of the requested content can be monitored. + /// public string? Location { get; } /// diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index 2beefab21428..e2502e4605ff 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -19,10 +19,14 @@ public BadRequestObjectHttpResult(object? error) Value = error; } - /// + /// + /// Gets the object result. + /// public object? Value { get; internal init; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status400BadRequest; /// diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index bbbb4edee533..3f0e192b7f4d 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -19,10 +19,14 @@ internal ConflictObjectHttpResult(object? error) Value = error; } - /// + /// + /// Gets the object result. + /// public object? Value { get; internal init; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status409Conflict; /// diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index fef5cccac177..ad98cfd0c993 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -42,7 +42,9 @@ public ContentHttpResult(string? content, string? contentType, int? statusCode) /// public string? ContentType { get; internal init; } - /// + /// + /// Gets the HTTP status code. + /// public int? StatusCode { get; internal init; } /// diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index 1467de8ce908..502a745c3f51 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -41,16 +41,24 @@ public CreatedAtRouteHttpResult( RouteValues = new RouteValueDictionary(routeValues); } - /// + /// + /// Gets the object result. + /// public object? Value { get; } - /// + /// + /// Gets the name of the route to use for generating the URL. + /// public string? RouteName { get; } - /// + /// + /// Gets the route data to use for generating the URL. + /// public RouteValueDictionary? RouteValues { get; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status201Created; /// diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index f6c1f7b8e54a..26d4ca404226 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -46,10 +46,14 @@ public CreatedHttpResult(Uri locationUri, object? value) } } - /// + /// + /// Gets the object result. + /// public object? Value { get; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status201Created; /// diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index 409243b51a46..f663a6972515 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -66,22 +66,34 @@ public FileContentHttpResult( EntityTag = entityTag; } - /// + /// + /// Gets the Content-Type header for the response. + /// public string ContentType { get; internal set; } - /// + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// public string? FileDownloadName { get; internal set; } - /// + /// + /// Gets the last modified information associated with the file result. + /// public DateTimeOffset? LastModified { get; internal set; } - /// + /// + /// Gets the etag associated with the file result. + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// + /// + /// Gets the value that enables range processing for the file result. + /// public bool EnableRangeProcessing { get; internal init; } - /// + /// + /// Gets or sets the file length information . + /// public long? FileLength { get; internal set; } /// diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index 15bf2b1cbd02..73a492ba7aa9 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -75,22 +75,34 @@ public FileStreamHttpResult( EntityTag = entityTag; } - /// + /// + /// Gets the Content-Type header for the response. + /// public string ContentType { get; internal set; } - /// + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// public string? FileDownloadName { get; internal set; } - /// + /// + /// Gets the last modified information associated with the file result. + /// public DateTimeOffset? LastModified { get; internal set; } - /// + /// + /// Gets the etag associated with the file result. + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// + /// + /// Gets the value that enables range processing for the file result. + /// public bool EnableRangeProcessing { get; internal init; } - /// + /// + /// Gets or sets the file length information . + /// public long? FileLength { get; internal set; } /// diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index 412fb72ce031..6d996fed9b91 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -38,7 +38,9 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso /// public JsonSerializerOptions? JsonSerializerOptions { get; internal init; } - /// + /// + /// Gets the object result. + /// public object? Value { get; } /// @@ -46,7 +48,9 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso /// public string? ContentType { get; internal set; } - /// + /// + /// Gets the HTTP status code. + /// public int? StatusCode { get; } /// diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index bc10bc951b10..4efe5ad00943 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -19,7 +19,9 @@ public NoContentHttpResult() { } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status204NoContent; /// diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index 9d88349acb82..1daedd50868b 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -18,10 +18,14 @@ internal NotFoundObjectHttpResult(object? value) Value = value; } - /// + /// + /// Gets the object result. + /// public object? Value { get; internal init; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status404NotFound; /// diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index 6389531a13ed..658a7574c313 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -46,7 +46,9 @@ public ObjectHttpResult(object? value, int? statusCode, string? contentType) ContentType = contentType; } - /// + /// + /// Gets the object result. + /// public object? Value { get; internal init; } /// @@ -54,7 +56,9 @@ public ObjectHttpResult(object? value, int? statusCode, string? contentType) /// public string? ContentType { get; internal init; } - /// + /// + /// Gets the HTTP status code. + /// public int? StatusCode { get; internal init; } /// diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index 011f5b40e9e2..9674add82127 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -20,10 +20,14 @@ public OkObjectHttpResult(object? value) Value = value; } - /// + /// + /// Gets the object result. + /// public object? Value { get; internal init; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status200OK; /// diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index be7a25f0182e..d2b0d9651311 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -63,22 +63,34 @@ public PhysicalFileHttpResult( EntityTag = entityTag; } - /// + /// + /// Gets the Content-Type header for the response. + /// public string ContentType { get; internal set; } - /// + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// public string? FileDownloadName { get; internal set; } - /// + /// + /// Gets the last modified information associated with the file result. + /// public DateTimeOffset? LastModified { get; internal set; } - /// + /// + /// Gets the etag associated with the file result. + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// + /// + /// Gets the value that enables range processing for the file result. + /// public bool EnableRangeProcessing { get; internal init; } - /// + /// + /// Gets or sets the file length information . + /// public long? FileLength { get; internal set; } /// diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index eb622f9139e3..0b6b135edd52 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -31,7 +31,9 @@ public ProblemHttpResult(ProblemDetails problemDetails) /// public string ContentType => "application/problem+json"; - /// + /// + /// Gets the HTTP status code. + /// public int? StatusCode => ProblemDetails.Status; /// diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index f69cfa3307e8..6c18fee79a40 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -65,22 +65,34 @@ public PushStreamHttpResult( EntityTag = entityTag; } - /// + /// + /// Gets the Content-Type header for the response. + /// public string ContentType { get; internal set; } - /// + /// + /// Gets the file name that will be used in the Content-Disposition header of the response. + /// public string? FileDownloadName { get; internal set; } - /// + /// + /// Gets the last modified information associated with the file result. + /// public DateTimeOffset? LastModified { get; internal set; } - /// + /// + /// Gets the etag associated with the file result. + /// public EntityTagHeaderValue? EntityTag { get; internal init; } - /// + /// + /// Gets the value that enables range processing for the file result. + /// public bool EnableRangeProcessing { get; internal init; } - /// + /// + /// Gets or sets the file length information . + /// public long? FileLength { get; internal set; } /// diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index 9fdbe2aaaf76..127b0a39eda9 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -77,16 +77,24 @@ public RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, b _url = url; } - /// + /// + /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. + /// public bool Permanent { get; } - /// + /// + /// Gets an indication that the redirect preserves the initial request method. + /// public bool PreserveMethod { get; } - /// + /// + /// Gets the URL to redirect to. + /// public string? Url => _url; - /// + /// + /// Gets an indication that only local URLs are accepted. + /// public bool AcceptLocalUrlOnly { get; } /// diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index 7d146a1a2034..ae6b4148b8da 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -130,19 +130,29 @@ public RedirectToRouteHttpResult( Fragment = fragment; } - /// + /// + /// Gets the name of the route to use for generating the URL. + /// public string? RouteName { get; } - /// + /// + /// Gets the route data to use for generating the URL. + /// public RouteValueDictionary? RouteValues { get; } - /// + /// + /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. + /// public bool Permanent { get; } - /// + /// + /// Gets an indication that the redirect preserves the initial request method. + /// public bool PreserveMethod { get; } - /// + /// + /// Gets the fragment to add to the URL. + /// public string? Fragment { get; } /// diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index 31e4f2006e5f..a53e0f093ada 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -22,7 +22,9 @@ internal StatusCodeHttpResult(int statusCode) StatusCode = statusCode; } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode { get; } /// diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index e676e9196c6d..e32359806223 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -19,7 +19,9 @@ public UnauthorizedHttpResult() { } - /// + /// + /// Gets the HTTP status code. + /// public int StatusCode => StatusCodes.Status401Unauthorized; /// From 6171f15767b9c9cd4c441a9eb7183bf650859e56 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 10:02:34 -0700 Subject: [PATCH 15/29] Making EmptyResult public --- src/Http/Http.Results/src/PublicAPI.Unshipped.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index ce6efb3f0d4b..089e5ceca633 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -210,3 +210,4 @@ static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory conte static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.IO.Pipelines.PipeReader! pipeReader, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) -> Microsoft.AspNetCore.Http.IResult! +static readonly Microsoft.AspNetCore.Http.EmptyHttpResult.Instance -> Microsoft.AspNetCore.Http.EmptyHttpResult! From 6a807cf4e5beac536e76b230d00a1f0f02286252 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 10:05:57 -0700 Subject: [PATCH 16/29] Updating JsonHttpResult --- src/Http/Http.Results/src/EmptyHttpResult.cs | 2 +- src/Http/Http.Results/src/JsonHttpResult.cs | 29 +++++++++++++++++-- .../Http.Results/src/PublicAPI.Unshipped.txt | 2 ++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Http/Http.Results/src/EmptyHttpResult.cs b/src/Http/Http.Results/src/EmptyHttpResult.cs index 661b5577615b..29b44a2190ca 100644 --- a/src/Http/Http.Results/src/EmptyHttpResult.cs +++ b/src/Http/Http.Results/src/EmptyHttpResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Http; /// public sealed class EmptyHttpResult : IResult { - internal static readonly EmptyHttpResult Instance = new(); + public static readonly EmptyHttpResult Instance = new(); private EmptyHttpResult() { diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index 6d996fed9b91..6cef64ce78eb 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -16,7 +16,7 @@ public sealed class JsonHttpResult : IResult /// The value to format in the entity body. /// The serializer settings. public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) - : this(value, statusCode: null, jsonSerializerOptions: jsonSerializerOptions) + : this(value, statusCode: null, contentType: null, jsonSerializerOptions: jsonSerializerOptions) { } @@ -27,10 +27,35 @@ public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOption /// The HTTP status code of the response. /// The serializer settings. public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) + : this(value, statusCode: statusCode, contentType: null, jsonSerializerOptions: jsonSerializerOptions) + { + } + + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. + /// The value for the Content-Type header + /// The serializer settings. + public JsonHttpResult(object? value, string? contentType, JsonSerializerOptions? jsonSerializerOptions) + : this(value, statusCode: null, contentType: contentType, jsonSerializerOptions: jsonSerializerOptions) + { + + } + + /// + /// Initializes a new instance of the class with the values. + /// + /// The value to format in the entity body. + /// The HTTP status code of the response. + /// The serializer settings. + /// The value for the Content-Type header + public JsonHttpResult(object? value, int? statusCode, string? contentType, JsonSerializerOptions? jsonSerializerOptions) { Value = value; StatusCode = statusCode; JsonSerializerOptions = jsonSerializerOptions; + ContentType = contentType; } /// @@ -44,7 +69,7 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso public object? Value { get; } /// - /// Gets or sets the value for the Content-Type header. + /// Gets the value for the Content-Type header. /// public string? ContentType { get; internal set; } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 089e5ceca633..2c0f4bd07a47 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -96,6 +96,8 @@ Microsoft.AspNetCore.Http.JsonHttpResult.ContentType.get -> string? Microsoft.AspNetCore.Http.JsonHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, int? statusCode, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void +Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, int? statusCode, string? contentType, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void +Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, string? contentType, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void Microsoft.AspNetCore.Http.JsonHttpResult.JsonSerializerOptions.get -> System.Text.Json.JsonSerializerOptions? Microsoft.AspNetCore.Http.JsonHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.JsonHttpResult.Value.get -> object? From 5fa233d1fb0a4ae9d1784b3642c4b5f245a876e6 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 10:20:19 -0700 Subject: [PATCH 17/29] Adding missing ctor --- src/Http/Http.Results/src/NotFoundObjectHttpResult.cs | 2 +- src/Http/Http.Results/src/PublicAPI.Unshipped.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index 1daedd50868b..905efc6c70ff 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -13,7 +13,7 @@ public sealed class NotFoundObjectHttpResult : IResult /// Initializes a new instance of the class with the values. /// /// The value to format in the entity body. - internal NotFoundObjectHttpResult(object? value) + public NotFoundObjectHttpResult(object? value) { Value = value; } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 2c0f4bd07a47..7b89ad3096e8 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -107,6 +107,7 @@ Microsoft.AspNetCore.Http.NoContentHttpResult.NoContentHttpResult() -> void Microsoft.AspNetCore.Http.NoContentHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.NotFoundObjectHttpResult Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.NotFoundObjectHttpResult(object? value) -> void Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.OkObjectHttpResult From dcf745b5d056f5cf831fa4d4273e51f22d064dc0 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 10:33:35 -0700 Subject: [PATCH 18/29] Adding missing ctor --- src/Http/Http.Results/src/ChallengeHttpResult.cs | 4 ++-- src/Http/Http.Results/src/ConflictObjectHttpResult.cs | 2 +- src/Http/Http.Results/src/PublicAPI.Unshipped.txt | 2 ++ src/Http/Http.Results/src/SignOutHttpResult.cs | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Http/Http.Results/src/ChallengeHttpResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs index 2c5f3e904370..c4729a5561ec 100644 --- a/src/Http/Http.Results/src/ChallengeHttpResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -78,12 +78,12 @@ public ChallengeHttpResult(IList authenticationSchemes, AuthenticationPr } /// - /// Gets or sets the authentication schemes that are challenged. + /// Gets the authentication schemes that are challenged. /// public IReadOnlyList AuthenticationSchemes { get; internal init; } = Array.Empty(); /// - /// Gets or sets the used to perform the sign-out operation. + /// Gets the used to perform the sign-out operation. /// public AuthenticationProperties? Properties { get; internal init; } diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index 3f0e192b7f4d..7dbc0c129c76 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -14,7 +14,7 @@ public sealed class ConflictObjectHttpResult : IResult /// provided. /// /// The error content to format in the entity body. - internal ConflictObjectHttpResult(object? error) + public ConflictObjectHttpResult(object? error) { Value = error; } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 7b89ad3096e8..a43ddc3a694c 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -30,6 +30,7 @@ Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(string! authen Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? Microsoft.AspNetCore.Http.ConflictObjectHttpResult +Microsoft.AspNetCore.Http.ConflictObjectHttpResult.ConflictObjectHttpResult(object? error) -> void Microsoft.AspNetCore.Http.ConflictObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ConflictObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.ConflictObjectHttpResult.Value.get -> object? @@ -188,6 +189,7 @@ Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(string! authentica Microsoft.AspNetCore.Http.StatusCodeHttpResult Microsoft.AspNetCore.Http.StatusCodeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.get -> int +Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCodeHttpResult(int statusCode) -> void Microsoft.AspNetCore.Http.UnauthorizedHttpResult Microsoft.AspNetCore.Http.UnauthorizedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.UnauthorizedHttpResult.StatusCode.get -> int diff --git a/src/Http/Http.Results/src/SignOutHttpResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs index 8b48fe72b290..7780663e559e 100644 --- a/src/Http/Http.Results/src/SignOutHttpResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -80,12 +80,12 @@ public SignOutHttpResult(IList authenticationSchemes, AuthenticationProp } /// - /// Gets or sets the authentication schemes that are challenged. + /// Gets the authentication schemes that are challenged. /// public IReadOnlyList AuthenticationSchemes { get; internal init; } /// - /// Gets or sets the used to perform the sign-out operation. + /// Gets the used to perform the sign-out operation. /// public AuthenticationProperties? Properties { get; internal init; } From 3d01bb63db29799713b97a42a414588f57ef8f2c Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 10:34:30 -0700 Subject: [PATCH 19/29] Adding missing ctor --- src/Http/Http.Results/src/ForbidHttpResult.cs | 4 ++-- src/Http/Http.Results/src/StatusCodeHttpResult.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/Http.Results/src/ForbidHttpResult.cs b/src/Http/Http.Results/src/ForbidHttpResult.cs index 3ab413dc7729..59794d8c2949 100644 --- a/src/Http/Http.Results/src/ForbidHttpResult.cs +++ b/src/Http/Http.Results/src/ForbidHttpResult.cs @@ -78,12 +78,12 @@ public ForbidHttpResult(IList authenticationSchemes, AuthenticationPrope } /// - /// Gets or sets the authentication schemes that are challenged. + /// Gets the authentication schemes that are challenged. /// public IReadOnlyList AuthenticationSchemes { get; internal init; } /// - /// Gets or sets the used to perform the authentication challenge. + /// Gets the used to perform the authentication challenge. /// public AuthenticationProperties? Properties { get; internal init; } diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index a53e0f093ada..b0f2da07dc5c 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -17,7 +17,7 @@ public sealed partial class StatusCodeHttpResult : IResult /// with the given . /// /// The HTTP status code of the response. - internal StatusCodeHttpResult(int statusCode) + public StatusCodeHttpResult(int statusCode) { StatusCode = statusCode; } From e05b04fe832457832e6a9adee56357539c1024df Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 10:44:10 -0700 Subject: [PATCH 20/29] Adding misssed xml comment --- src/Http/Http.Results/src/EmptyHttpResult.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Http/Http.Results/src/EmptyHttpResult.cs b/src/Http/Http.Results/src/EmptyHttpResult.cs index 29b44a2190ca..5924bf8f54ff 100644 --- a/src/Http/Http.Results/src/EmptyHttpResult.cs +++ b/src/Http/Http.Results/src/EmptyHttpResult.cs @@ -9,6 +9,9 @@ namespace Microsoft.AspNetCore.Http; /// public sealed class EmptyHttpResult : IResult { + /// + /// Gets an instance of . + /// public static readonly EmptyHttpResult Instance = new(); private EmptyHttpResult() From 9c628aa155b2cb304fc9ea0d0876389f1cba6302 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 11:38:09 -0700 Subject: [PATCH 21/29] Removing configureResponseHeader --- .../src/AcceptedAtRouteHttpResult.cs | 10 ++++------ src/Http/Http.Results/src/AcceptedHttpResult.cs | 14 ++++++-------- .../src/CreatedAtRouteHttpResult.cs | 17 +++++++---------- src/Http/Http.Results/src/CreatedHttpResult.cs | 14 ++++++-------- src/Http/Http.Results/src/HttpResultsWriter.cs | 5 +---- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index 5daf87eda2b7..c74140cb69a9 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -64,13 +64,10 @@ public AcceptedAtRouteHttpResult( /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode, configureResponseHeader: ConfigureResponseHeaders); - - private void ConfigureResponseHeaders(HttpContext context) { - var linkGenerator = context.RequestServices.GetRequiredService(); + var linkGenerator = httpContext.RequestServices.GetRequiredService(); var url = linkGenerator.GetUriByRouteValues( - context, + httpContext, RouteName, RouteValues, fragment: FragmentString.Empty); @@ -80,6 +77,7 @@ private void ConfigureResponseHeaders(HttpContext context) throw new InvalidOperationException("No route matches the supplied values."); } - context.Response.Headers.Location = url; + httpContext.Response.Headers.Location = url; + return HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } } diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index cede3cc196e4..0f19b9c4da71 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -67,16 +67,14 @@ public AcceptedHttpResult(Uri locationUri, object? value) /// public Task ExecuteAsync(HttpContext httpContext) { + if (!string.IsNullOrEmpty(Location)) + { + httpContext.Response.Headers.Location = Location; + } + return HttpResultsWriter.WriteResultAsJsonAsync( httpContext, Value, - StatusCode, - configureResponseHeader: (context) => - { - if (!string.IsNullOrEmpty(Location)) - { - context.Response.Headers.Location = Location; - } - }); + StatusCode); } } diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index 502a745c3f51..59c0d1de9bed 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -63,17 +63,10 @@ public CreatedAtRouteHttpResult( /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync( - httpContext, - Value, - StatusCode, - configureResponseHeader: ConfigureResponseHeaders); - - private void ConfigureResponseHeaders(HttpContext context) { - var linkGenerator = context.RequestServices.GetRequiredService(); + var linkGenerator = httpContext.RequestServices.GetRequiredService(); var url = linkGenerator.GetUriByRouteValues( - context, + httpContext, RouteName, RouteValues, fragment: FragmentString.Empty); @@ -83,6 +76,10 @@ private void ConfigureResponseHeaders(HttpContext context) throw new InvalidOperationException("No route matches the supplied values."); } - context.Response.Headers.Location = url; + httpContext.Response.Headers.Location = url; + return HttpResultsWriter.WriteResultAsJsonAsync( + httpContext, + Value, + StatusCode); } } diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index 26d4ca404226..a3cf4fba6980 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -62,16 +62,14 @@ public CreatedHttpResult(Uri locationUri, object? value) /// public Task ExecuteAsync(HttpContext httpContext) { + if (!string.IsNullOrEmpty(Location)) + { + httpContext.Response.Headers.Location = Location; + } + return HttpResultsWriter.WriteResultAsJsonAsync( httpContext, Value, - StatusCode, - configureResponseHeader: (context) => - { - if (!string.IsNullOrEmpty(Location)) - { - context.Response.Headers.Location = Location; - } - }); + StatusCode); } } diff --git a/src/Http/Http.Results/src/HttpResultsWriter.cs b/src/Http/Http.Results/src/HttpResultsWriter.cs index e97d5a8f4075..7e52e6d718cd 100644 --- a/src/Http/Http.Results/src/HttpResultsWriter.cs +++ b/src/Http/Http.Results/src/HttpResultsWriter.cs @@ -22,8 +22,7 @@ public static Task WriteResultAsJsonAsync( object? value, int? statusCode, string? contentType = null, - JsonSerializerOptions? jsonSerializerOptions = null, - Action? configureResponseHeader = null) + JsonSerializerOptions? jsonSerializerOptions = null) { Log.WritingResultAsJson(GetLogger(httpContext), value, statusCode); @@ -38,8 +37,6 @@ public static Task WriteResultAsJsonAsync( httpContext.Response.StatusCode = code; } - configureResponseHeader?.Invoke(httpContext); - if (value is null) { return Task.CompletedTask; From eada9b44a92e750d772cfee0c66d72baa8d340b2 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 13:05:31 -0700 Subject: [PATCH 22/29] PR Feedback --- .../src/AcceptedAtRouteHttpResult.cs | 7 ++++--- .../Http.Results/src/AcceptedHttpResult.cs | 4 +++- .../src/BadRequestObjectHttpResult.cs | 3 ++- .../src/ConflictObjectHttpResult.cs | 3 ++- .../Http.Results/src/ContentHttpResult.cs | 2 +- .../src/CreatedAtRouteHttpResult.cs | 3 ++- .../Http.Results/src/CreatedHttpResult.cs | 4 +++- .../Http.Results/src/FileContentHttpResult.cs | 2 +- .../Http.Results/src/FileStreamHttpResult.cs | 2 +- ...pResultsWriter.cs => HttpResultsHelper.cs} | 20 ++++++++++--------- src/Http/Http.Results/src/JsonHttpResult.cs | 3 ++- .../Http.Results/src/NoContentHttpResult.cs | 2 +- .../src/NotFoundObjectHttpResult.cs | 3 ++- src/Http/Http.Results/src/ObjectHttpResult.cs | 4 ++-- .../Http.Results/src/OkObjectHttpResult.cs | 3 ++- .../src/PhysicalFileHttpResult.cs | 2 +- .../Http.Results/src/ProblemHttpResult.cs | 3 ++- .../Http.Results/src/PublicAPI.Unshipped.txt | 2 +- .../Http.Results/src/PushStreamHttpResult.cs | 2 +- .../Http.Results/src/StatusCodeHttpResult.cs | 2 +- .../src/UnauthorizedHttpResult.cs | 2 +- .../UnprocessableEntityObjectHttpResult.cs | 3 ++- .../Http.Results/src/VirtualFileHttpResult.cs | 2 +- .../test/AcceptedAtRouteResultTests.cs | 18 +++++++++++++++++ .../Http.Results/test/AcceptedResultTests.cs | 14 +++++++++++++ .../test/BadRequestObjectResultTests.cs | 13 ++++++++++++ .../test/ConflictObjectResultTest.cs | 15 ++++++++++++++ .../test/CreatedAtRouteResultTests.cs | 17 ++++++++++++++++ .../Http.Results/test/CreatedResultTest.cs | 14 +++++++++++++ .../test/NotFoundObjectResultTest.cs | 13 ++++++++++++ .../Http.Results/test/OkObjectResultTest.cs | 13 ++++++++++++ .../UnprocessableEntityObjectResultTests.cs | 13 ++++++++++++ 32 files changed, 179 insertions(+), 34 deletions(-) rename src/Http/Http.Results/src/{HttpResultsWriter.cs => HttpResultsHelper.cs} (96%) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index c74140cb69a9..b4b7633afb2b 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -40,6 +40,7 @@ public AcceptedAtRouteHttpResult( Value = value; RouteName = routeName; RouteValues = new RouteValueDictionary(routeValues); + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -55,7 +56,7 @@ public AcceptedAtRouteHttpResult( /// /// Gets the route data to use for generating the URL. /// - public RouteValueDictionary? RouteValues { get; } + public RouteValueDictionary RouteValues { get; } /// /// Gets the HTTP status code. @@ -66,7 +67,7 @@ public AcceptedAtRouteHttpResult( public Task ExecuteAsync(HttpContext httpContext) { var linkGenerator = httpContext.RequestServices.GetRequiredService(); - var url = linkGenerator.GetUriByRouteValues( + var url = linkGenerator.GetUriByAddress( httpContext, RouteName, RouteValues, @@ -78,6 +79,6 @@ public Task ExecuteAsync(HttpContext httpContext) } httpContext.Response.Headers.Location = url; - return HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + return HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } } diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index 0f19b9c4da71..1fa32c0b53ed 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -22,6 +22,7 @@ public AcceptedHttpResult(string? location, object? value) { Value = value; Location = location; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -33,6 +34,7 @@ public AcceptedHttpResult(string? location, object? value) public AcceptedHttpResult(Uri locationUri, object? value) { Value = value; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); if (locationUri == null) { @@ -72,7 +74,7 @@ public Task ExecuteAsync(HttpContext httpContext) httpContext.Response.Headers.Location = Location; } - return HttpResultsWriter.WriteResultAsJsonAsync( + return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, Value, StatusCode); diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index e2502e4605ff..2baffea8dd30 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -17,6 +17,7 @@ public sealed class BadRequestObjectHttpResult : IResult public BadRequestObjectHttpResult(object? error) { Value = error; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -31,5 +32,5 @@ public BadRequestObjectHttpResult(object? error) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index 7dbc0c129c76..e7b196b3de03 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -17,6 +17,7 @@ public sealed class ConflictObjectHttpResult : IResult public ConflictObjectHttpResult(object? error) { Value = error; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -31,5 +32,5 @@ public ConflictObjectHttpResult(object? error) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index ad98cfd0c993..640f0c87af8f 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -53,5 +53,5 @@ public ContentHttpResult(string? content, string? contentType, int? statusCode) /// The for the current request. /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsContentAsync(httpContext, Content, StatusCode, ContentType); + => HttpResultsHelper.WriteResultAsContentAsync(httpContext, Content, StatusCode, ContentType); } diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index 59c0d1de9bed..b35ce9f196aa 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -39,6 +39,7 @@ public CreatedAtRouteHttpResult( Value = value; RouteName = routeName; RouteValues = new RouteValueDictionary(routeValues); + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -77,7 +78,7 @@ public Task ExecuteAsync(HttpContext httpContext) } httpContext.Response.Headers.Location = url; - return HttpResultsWriter.WriteResultAsJsonAsync( + return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, Value, StatusCode); diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index a3cf4fba6980..18bba7af29c8 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -19,6 +19,7 @@ public CreatedHttpResult(string location, object? value) { Value = value; Location = location; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -30,6 +31,7 @@ public CreatedHttpResult(string location, object? value) public CreatedHttpResult(Uri locationUri, object? value) { Value = value; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); if (locationUri == null) { @@ -67,7 +69,7 @@ public Task ExecuteAsync(HttpContext httpContext) httpContext.Response.Headers.Location = Location; } - return HttpResultsWriter.WriteResultAsJsonAsync( + return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, Value, StatusCode); diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index f663a6972515..d54daf0907bd 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -103,7 +103,7 @@ public FileContentHttpResult( /// public Task ExecuteAsync(HttpContext httpContext) => - HttpResultsWriter.WriteResultAsFileAsync( + HttpResultsHelper.WriteResultAsFileAsync( httpContext, FileDownloadName, FileLength, diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index 73a492ba7aa9..030fba7befe7 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -115,7 +115,7 @@ public async Task ExecuteAsync(HttpContext httpContext) { await using (FileStream) { - await HttpResultsWriter.WriteResultAsFileAsync( + await HttpResultsHelper.WriteResultAsFileAsync( httpContext, FileDownloadName, FileLength, diff --git a/src/Http/Http.Results/src/HttpResultsWriter.cs b/src/Http/Http.Results/src/HttpResultsHelper.cs similarity index 96% rename from src/Http/Http.Results/src/HttpResultsWriter.cs rename to src/Http/Http.Results/src/HttpResultsHelper.cs index 7e52e6d718cd..03c300e1a40e 100644 --- a/src/Http/Http.Results/src/HttpResultsWriter.cs +++ b/src/Http/Http.Results/src/HttpResultsHelper.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -internal static partial class HttpResultsWriter +internal static partial class HttpResultsHelper { private const string DefaultContentType = "text/plain; charset=utf-8"; private static readonly Encoding DefaultEncoding = Encoding.UTF8; @@ -26,12 +26,6 @@ public static Task WriteResultAsJsonAsync( { Log.WritingResultAsJson(GetLogger(httpContext), value, statusCode); - if (value is ProblemDetails problemDetails) - { - ApplyProblemDetailsDefaults(problemDetails, statusCode); - statusCode ??= problemDetails.Status; - } - if (statusCode is { } code) { httpContext.Response.StatusCode = code; @@ -131,6 +125,14 @@ public static Task WriteResultAsFileAsync( return writeOperation(httpContext, range, rangeLength); } + public static void ApplyProblemDetailsDefaultsIfNeeded(object? value, int? statusCode) + { + if (value is ProblemDetails problemDetails) + { + ApplyProblemDetailsDefaults(problemDetails, statusCode); + } + } + public static void ApplyProblemDetailsDefaults(ProblemDetails problemDetails, int? statusCode) { // We allow StatusCode to be specified either on ProblemDetails or on the ObjectResult and use it to configure the other. @@ -160,7 +162,7 @@ public static void ApplyProblemDetailsDefaults(ProblemDetails problemDetails, in private static ILogger GetLogger(HttpContext httpContext) { var factory = httpContext.RequestServices.GetRequiredService(); - var logger = factory.CreateLogger(typeof(HttpResultsWriter)); + var logger = factory.CreateLogger(typeof(HttpResultsHelper)); return logger; } @@ -199,7 +201,7 @@ public static void WritingResultAsFile(ILogger logger, string fileDownloadName) EventName = "WritingResultAsContent")] public static partial void WritingResultAsContent(ILogger logger, string contentType); - [LoggerMessage(3, LogLevel.Information, "Writing value of type '{Type}'as Json with status code '{StatusCode}'.", + [LoggerMessage(3, LogLevel.Information, "Writing value of type '{Type}' as Json with status code '{StatusCode}'.", EventName = "WritingResultAsJson", SkipEnabledCheck = true)] private static partial void WritingResultAsJson(ILogger logger, string type, int statusCode); diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index 6cef64ce78eb..abe1788b38f9 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -56,6 +56,7 @@ public JsonHttpResult(object? value, int? statusCode, string? contentType, JsonS StatusCode = statusCode; JsonSerializerOptions = jsonSerializerOptions; ContentType = contentType; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -80,7 +81,7 @@ public JsonHttpResult(object? value, int? statusCode, string? contentType, JsonS /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync( + => HttpResultsHelper.WriteResultAsJsonAsync( httpContext, Value, StatusCode, diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index 4efe5ad00943..957ee5b99b04 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -28,7 +28,7 @@ public NoContentHttpResult() public Task ExecuteAsync(HttpContext httpContext) { var logger = httpContext.RequestServices.GetRequiredService>(); - HttpResultsWriter.Log.WritingResultAsStatusCode(logger, StatusCode); + HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode); httpContext.Response.StatusCode = StatusCode; diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index 905efc6c70ff..e0d5b04a1f0e 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -16,6 +16,7 @@ public sealed class NotFoundObjectHttpResult : IResult public NotFoundObjectHttpResult(object? value) { Value = value; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -30,5 +31,5 @@ public NotFoundObjectHttpResult(object? value) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index 658a7574c313..54453343b070 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -38,7 +38,7 @@ public ObjectHttpResult(object? value, int? statusCode, string? contentType) if (value is ProblemDetails problemDetails) { - HttpResultsWriter.ApplyProblemDetailsDefaults(problemDetails, statusCode); + HttpResultsHelper.ApplyProblemDetailsDefaults(problemDetails, statusCode); statusCode ??= problemDetails.Status; } @@ -63,5 +63,5 @@ public ObjectHttpResult(object? value, int? statusCode, string? contentType) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode, ContentType); + => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode, ContentType); } diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index 9674add82127..62b8b22af95c 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -18,6 +18,7 @@ public sealed class OkObjectHttpResult : IResult public OkObjectHttpResult(object? value) { Value = value; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -32,5 +33,5 @@ public OkObjectHttpResult(object? value) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index d2b0d9651311..d7784f30a688 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -114,7 +114,7 @@ public Task ExecuteAsync(HttpContext httpContext) LastModified ??= fileInfo.LastWriteTimeUtc; FileLength = fileInfo.Length; - return HttpResultsWriter.WriteResultAsFileAsync( + return HttpResultsHelper.WriteResultAsFileAsync( httpContext, FileDownloadName, FileLength, diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 0b6b135edd52..56dba6c56ef7 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -19,6 +19,7 @@ public sealed class ProblemHttpResult : IResult public ProblemHttpResult(ProblemDetails problemDetails) { ProblemDetails = problemDetails; + HttpResultsHelper.ApplyProblemDetailsDefaults(ProblemDetails, statusCode: null); } /// @@ -38,7 +39,7 @@ public ProblemHttpResult(ProblemDetails problemDetails) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync( + => HttpResultsHelper.WriteResultAsJsonAsync( httpContext, value: ProblemDetails, StatusCode, diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index a43ddc3a694c..8fd7b52f34bd 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -4,7 +4,7 @@ Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.AcceptedAtRouteHttpResult(ob Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.AcceptedAtRouteHttpResult(string? routeName, object? routeValues, object? value) -> void Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteName.get -> string? -Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? +Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary! Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.AcceptedHttpResult diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index 6c18fee79a40..0671d042f1ab 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -97,7 +97,7 @@ public PushStreamHttpResult( /// public Task ExecuteAsync(HttpContext httpContext) => - HttpResultsWriter.WriteResultAsFileAsync( + HttpResultsHelper.WriteResultAsFileAsync( httpContext, FileDownloadName, FileLength, diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index b0f2da07dc5c..0af2deced38c 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -35,7 +35,7 @@ public StatusCodeHttpResult(int statusCode) public Task ExecuteAsync(HttpContext httpContext) { var logger = httpContext.RequestServices.GetRequiredService>(); - HttpResultsWriter.Log.WritingResultAsStatusCode(logger, StatusCode); + HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode); httpContext.Response.StatusCode = StatusCode; diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index e32359806223..dc28aa822130 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -28,7 +28,7 @@ public UnauthorizedHttpResult() public Task ExecuteAsync(HttpContext httpContext) { var logger = httpContext.RequestServices.GetRequiredService>(); - HttpResultsWriter.Log.WritingResultAsStatusCode(logger, StatusCode); + HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode); httpContext.Response.StatusCode = StatusCode; diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index 45520545bab8..a4cdd51d646a 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -17,6 +17,7 @@ public sealed class UnprocessableEntityObjectHttpResult : IResult public UnprocessableEntityObjectHttpResult(object? error) { Value = error; + HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } /// @@ -27,5 +28,5 @@ public UnprocessableEntityObjectHttpResult(object? error) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsWriter.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index f4aeb073c3ee..a6958c71f330 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -113,7 +113,7 @@ public Task ExecuteAsync(HttpContext httpContext) LastModified = LastModified ?? fileInfo.LastModified; FileLength = fileInfo.Length; - return HttpResultsWriter.WriteResultAsFileAsync( + return HttpResultsHelper.WriteResultAsFileAsync( httpContext, FileDownloadName, FileLength, diff --git a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs index 58eb0cf2f70b..ae0b8f754085 100644 --- a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs @@ -10,6 +10,24 @@ namespace Microsoft.AspNetCore.Http.Result; public class AcceptedAtRouteResultTests { + [Fact] + public void AcceptedAtRouteResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var routeValues = new RouteValueDictionary(new Dictionary() + { + { "test", "case" }, + { "sample", "route" } + }); + var obj = new HttpValidationProblemDetails(); + var result = new AcceptedAtRouteHttpResult(routeValues, obj); + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Equal(StatusCodes.Status202Accepted, obj.Status); + Assert.Equal(obj, result.Value); + } + [Fact] public async Task ExecuteResultAsync_FormatsData() { diff --git a/src/Http/Http.Results/test/AcceptedResultTests.cs b/src/Http/Http.Results/test/AcceptedResultTests.cs index 203e8ea13d1f..9cf5eb6bbfb8 100644 --- a/src/Http/Http.Results/test/AcceptedResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedResultTests.cs @@ -40,6 +40,20 @@ public async Task ExecuteResultAsync_SetsStatusCodeAndLocationHeader() Assert.Equal(expectedUrl, httpContext.Response.Headers["Location"]); } + [Fact] + public void AcceptedResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var expectedUrl = "testAction"; + var obj = new HttpValidationProblemDetails(); + var result = new AcceptedHttpResult(expectedUrl, obj); + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Equal(StatusCodes.Status202Accepted, obj.Status); + Assert.Equal(obj, result.Value); + } + private static HttpContext GetHttpContext() { var httpContext = new DefaultHttpContext(); diff --git a/src/Http/Http.Results/test/BadRequestObjectResultTests.cs b/src/Http/Http.Results/test/BadRequestObjectResultTests.cs index a9868e8467d7..dea5e8aa94ac 100644 --- a/src/Http/Http.Results/test/BadRequestObjectResultTests.cs +++ b/src/Http/Http.Results/test/BadRequestObjectResultTests.cs @@ -16,4 +16,17 @@ public void BadRequestObjectResult_SetsStatusCodeAndValue() Assert.Equal(StatusCodes.Status400BadRequest, badRequestObjectResult.StatusCode); Assert.Equal(obj, badRequestObjectResult.Value); } + + [Fact] + public void BadRequestObjectResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var obj = new HttpValidationProblemDetails(); + var result = new BadRequestObjectHttpResult(obj); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); + Assert.Equal(StatusCodes.Status400BadRequest, obj.Status); + Assert.Equal(obj, result.Value); + } } diff --git a/src/Http/Http.Results/test/ConflictObjectResultTest.cs b/src/Http/Http.Results/test/ConflictObjectResultTest.cs index 8f2de2f0f90d..3472a3e07b29 100644 --- a/src/Http/Http.Results/test/ConflictObjectResultTest.cs +++ b/src/Http/Http.Results/test/ConflictObjectResultTest.cs @@ -3,6 +3,8 @@ namespace Microsoft.AspNetCore.Http.Result; +using Microsoft.AspNetCore.Mvc; + public class ConflictObjectResultTest { [Fact] @@ -16,4 +18,17 @@ public void ConflictObjectResult_SetsStatusCodeAndValue() Assert.Equal(StatusCodes.Status409Conflict, conflictObjectResult.StatusCode); Assert.Equal(obj, conflictObjectResult.Value); } + + [Fact] + public void ConflictObjectResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var obj = new ProblemDetails(); + var conflictObjectResult = new ConflictObjectHttpResult(obj); + + // Assert + Assert.Equal(StatusCodes.Status409Conflict, conflictObjectResult.StatusCode); + Assert.Equal(StatusCodes.Status409Conflict, obj.Status); + Assert.Equal(obj, conflictObjectResult.Value); + } } diff --git a/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs b/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs index 7f6a45787660..5fd0575a3c35 100644 --- a/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs +++ b/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs @@ -11,6 +11,23 @@ namespace Microsoft.AspNetCore.Http.Result; public partial class CreatedAtRouteResultTests { + [Fact] + public void CreatedAtRouteResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var routeValues = new RouteValueDictionary(new Dictionary() + { + { "test", "case" }, + { "sample", "route" } + }); + var obj = new HttpValidationProblemDetails(); + var result = new CreatedAtRouteHttpResult(routeValues, obj); + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(StatusCodes.Status201Created, obj.Status); + Assert.Equal(obj, result.Value); + } public static IEnumerable CreatedAtRouteData { get diff --git a/src/Http/Http.Results/test/CreatedResultTest.cs b/src/Http/Http.Results/test/CreatedResultTest.cs index 447c7856f65e..2bc4b0ffa180 100644 --- a/src/Http/Http.Results/test/CreatedResultTest.cs +++ b/src/Http/Http.Results/test/CreatedResultTest.cs @@ -9,6 +9,20 @@ namespace Microsoft.AspNetCore.Http.Result; public class CreatedResultTests { + [Fact] + public void CreatedResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var expectedUrl = "testAction"; + var obj = new HttpValidationProblemDetails(); + var result = new CreatedHttpResult(expectedUrl, obj); + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(StatusCodes.Status201Created, obj.Status); + Assert.Equal(obj, result.Value); + } + [Fact] public void CreatedResult_SetsLocation() { diff --git a/src/Http/Http.Results/test/NotFoundObjectResultTest.cs b/src/Http/Http.Results/test/NotFoundObjectResultTest.cs index a5361f6aa2b9..b9c4426e1b40 100644 --- a/src/Http/Http.Results/test/NotFoundObjectResultTest.cs +++ b/src/Http/Http.Results/test/NotFoundObjectResultTest.cs @@ -9,6 +9,19 @@ namespace Microsoft.AspNetCore.Http.Result; public class NotFoundObjectResultTest { + [Fact] + public void NotFoundObjectResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var obj = new HttpValidationProblemDetails(); + var result = new NotFoundObjectHttpResult(obj); + + // Assert + Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); + Assert.Equal(StatusCodes.Status404NotFound, obj.Status); + Assert.Equal(obj, result.Value); + } + [Fact] public void NotFoundObjectResult_InitializesStatusCode() { diff --git a/src/Http/Http.Results/test/OkObjectResultTest.cs b/src/Http/Http.Results/test/OkObjectResultTest.cs index f9c8c369b723..bb19a01aecca 100644 --- a/src/Http/Http.Results/test/OkObjectResultTest.cs +++ b/src/Http/Http.Results/test/OkObjectResultTest.cs @@ -23,6 +23,19 @@ public async Task OkObjectResult_SetsStatusCodeAndValue() Assert.Equal(StatusCodes.Status200OK, httpContext.Response.StatusCode); } + [Fact] + public void OkObjectResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var obj = new HttpValidationProblemDetails(); + var result = new OkObjectHttpResult(obj); + + // Assert + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + Assert.Equal(StatusCodes.Status200OK, obj.Status); + Assert.Equal(obj, result.Value); + } + private static HttpContext GetHttpContext() { var httpContext = new DefaultHttpContext(); diff --git a/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs b/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs index 11662d70b557..ffc95adfeb62 100644 --- a/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs +++ b/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs @@ -5,6 +5,19 @@ namespace Microsoft.AspNetCore.Http.Result; public class UnprocessableEntityObjectResultTests { + [Fact] + public void NotFoundObjectResult_ProblemDetails_SetsStatusCodeAndValue() + { + // Arrange & Act + var obj = new HttpValidationProblemDetails(); + var result = new UnprocessableEntityObjectHttpResult(obj); + + // Assert + Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); + Assert.Equal(StatusCodes.Status422UnprocessableEntity, obj.Status); + Assert.Equal(obj, result.Value); + } + [Fact] public void UnprocessableEntityObjectResult_SetsStatusCodeAndValue() { From ff3cb765a659dc57c3f196a60272aae71a2eca19 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 13:31:26 -0700 Subject: [PATCH 23/29] Moving ctors to internal --- .../src/AcceptedAtRouteHttpResult.cs | 4 +- .../Http.Results/src/AcceptedHttpResult.cs | 4 +- .../src/BadRequestObjectHttpResult.cs | 2 +- .../Http.Results/src/ChallengeHttpResult.cs | 12 ++-- .../src/ConflictObjectHttpResult.cs | 2 +- .../Http.Results/src/ContentHttpResult.cs | 4 +- .../src/CreatedAtRouteHttpResult.cs | 4 +- .../Http.Results/src/CreatedHttpResult.cs | 4 +- src/Http/Http.Results/src/EmptyHttpResult.cs | 10 +-- .../Http.Results/src/FileContentHttpResult.cs | 6 +- .../Http.Results/src/FileStreamHttpResult.cs | 6 +- src/Http/Http.Results/src/ForbidHttpResult.cs | 12 ++-- src/Http/Http.Results/src/JsonHttpResult.cs | 8 +-- .../Http.Results/src/NoContentHttpResult.cs | 2 +- .../src/NotFoundObjectHttpResult.cs | 2 +- src/Http/Http.Results/src/ObjectHttpResult.cs | 6 +- .../Http.Results/src/OkObjectHttpResult.cs | 2 +- .../src/PhysicalFileHttpResult.cs | 6 +- .../Http.Results/src/ProblemHttpResult.cs | 2 +- .../Http.Results/src/PublicAPI.Unshipped.txt | 71 +------------------ .../Http.Results/src/PushStreamHttpResult.cs | 6 +- .../Http.Results/src/RedirectHttpResult.cs | 8 +-- .../src/RedirectToRouteHttpResult.cs | 14 ++-- src/Http/Http.Results/src/SignInHttpResult.cs | 4 +- .../Http.Results/src/SignOutHttpResult.cs | 12 ++-- .../Http.Results/src/StatusCodeHttpResult.cs | 2 +- .../src/UnauthorizedHttpResult.cs | 2 +- .../UnprocessableEntityObjectHttpResult.cs | 2 +- .../Http.Results/src/VirtualFileHttpResult.cs | 6 +- .../Http.Results/test/NoContentResultTests.cs | 1 - 30 files changed, 78 insertions(+), 148 deletions(-) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index b4b7633afb2b..118b0fe4a1f3 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -20,7 +20,7 @@ public sealed class AcceptedAtRouteHttpResult : IResult /// /// The route data to use for generating the URL. /// The value to format in the entity body. - public AcceptedAtRouteHttpResult(object? routeValues, object? value) + internal AcceptedAtRouteHttpResult(object? routeValues, object? value) : this(routeName: null, routeValues: routeValues, value: value) { } @@ -32,7 +32,7 @@ public AcceptedAtRouteHttpResult(object? routeValues, object? value) /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - public AcceptedAtRouteHttpResult( + internal AcceptedAtRouteHttpResult( string? routeName, object? routeValues, object? value) diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index 1fa32c0b53ed..de8f27b9dc43 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -18,7 +18,7 @@ public sealed class AcceptedHttpResult : IResult /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - public AcceptedHttpResult(string? location, object? value) + internal AcceptedHttpResult(string? location, object? value) { Value = value; Location = location; @@ -31,7 +31,7 @@ public AcceptedHttpResult(string? location, object? value) /// /// The location at which the status of requested content can be monitored. /// The value to format in the entity body. - public AcceptedHttpResult(Uri locationUri, object? value) + internal AcceptedHttpResult(Uri locationUri, object? value) { Value = value; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index 2baffea8dd30..0f1ed6f5077c 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -14,7 +14,7 @@ public sealed class BadRequestObjectHttpResult : IResult /// provided. /// /// The error content to format in the entity body. - public BadRequestObjectHttpResult(object? error) + internal BadRequestObjectHttpResult(object? error) { Value = error; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/ChallengeHttpResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs index c4729a5561ec..34b45d14d4b6 100644 --- a/src/Http/Http.Results/src/ChallengeHttpResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -16,7 +16,7 @@ public sealed partial class ChallengeHttpResult : IResult /// /// Initializes a new instance of . /// - public ChallengeHttpResult() + internal ChallengeHttpResult() : this(Array.Empty()) { } @@ -26,7 +26,7 @@ public ChallengeHttpResult() /// specified authentication scheme. /// /// The authentication scheme to challenge. - public ChallengeHttpResult(string authenticationScheme) + internal ChallengeHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -36,7 +36,7 @@ public ChallengeHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to challenge. - public ChallengeHttpResult(IList authenticationSchemes) + internal ChallengeHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -47,7 +47,7 @@ public ChallengeHttpResult(IList authenticationSchemes) /// /// used to perform the authentication /// challenge. - public ChallengeHttpResult(AuthenticationProperties? properties) + internal ChallengeHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } @@ -59,7 +59,7 @@ public ChallengeHttpResult(AuthenticationProperties? properties) /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - public ChallengeHttpResult(string authenticationScheme, AuthenticationProperties? properties) + internal ChallengeHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -71,7 +71,7 @@ public ChallengeHttpResult(string authenticationScheme, AuthenticationProperties /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - public ChallengeHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + internal ChallengeHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index e7b196b3de03..2a94739033e6 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -14,7 +14,7 @@ public sealed class ConflictObjectHttpResult : IResult /// provided. /// /// The error content to format in the entity body. - public ConflictObjectHttpResult(object? error) + internal ConflictObjectHttpResult(object? error) { Value = error; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index 640f0c87af8f..b9da81036665 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -14,7 +14,7 @@ public sealed partial class ContentHttpResult : IResult /// /// The value to format in the entity body. /// The Content-Type header for the response - public ContentHttpResult(string? content, string? contentType) + internal ContentHttpResult(string? content, string? contentType) : this(content, contentType, statusCode: null) { } @@ -25,7 +25,7 @@ public ContentHttpResult(string? content, string? contentType) /// The value to format in the entity body. /// The HTTP status code of the response. /// The Content-Type header for the response - public ContentHttpResult(string? content, string? contentType, int? statusCode) + internal ContentHttpResult(string? content, string? contentType, int? statusCode) { Content = content; StatusCode = statusCode; diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index b35ce9f196aa..52c24ae4065d 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -19,7 +19,7 @@ public sealed class CreatedAtRouteHttpResult : IResult /// /// The route data to use for generating the URL. /// The value to format in the entity body. - public CreatedAtRouteHttpResult(object? routeValues, object? value) + internal CreatedAtRouteHttpResult(object? routeValues, object? value) : this(routeName: null, routeValues: routeValues, value: value) { } @@ -31,7 +31,7 @@ public CreatedAtRouteHttpResult(object? routeValues, object? value) /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The value to format in the entity body. - public CreatedAtRouteHttpResult( + internal CreatedAtRouteHttpResult( string? routeName, object? routeValues, object? value) diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index 18bba7af29c8..cb1aea18e534 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -15,7 +15,7 @@ public sealed class CreatedHttpResult : IResult /// /// The location at which the content has been created. /// The value to format in the entity body. - public CreatedHttpResult(string location, object? value) + internal CreatedHttpResult(string location, object? value) { Value = value; Location = location; @@ -28,7 +28,7 @@ public CreatedHttpResult(string location, object? value) /// /// The location at which the content has been created. /// The value to format in the entity body. - public CreatedHttpResult(Uri locationUri, object? value) + internal CreatedHttpResult(Uri locationUri, object? value) { Value = value; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/EmptyHttpResult.cs b/src/Http/Http.Results/src/EmptyHttpResult.cs index 5924bf8f54ff..30754c832cca 100644 --- a/src/Http/Http.Results/src/EmptyHttpResult.cs +++ b/src/Http/Http.Results/src/EmptyHttpResult.cs @@ -9,15 +9,15 @@ namespace Microsoft.AspNetCore.Http; /// public sealed class EmptyHttpResult : IResult { - /// - /// Gets an instance of . - /// - public static readonly EmptyHttpResult Instance = new(); - private EmptyHttpResult() { } + /// + /// Gets an instance of . + /// + public static EmptyHttpResult Instance => new(); + /// public Task ExecuteAsync(HttpContext httpContext) { diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index d54daf0907bd..ec52667f6d77 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -19,7 +19,7 @@ public sealed partial class FileContentHttpResult : IResult /// /// The bytes that represent the file contents. /// The Content-Type of the file. - public FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) + internal FileContentHttpResult(ReadOnlyMemory fileContents, string? contentType) : this(fileContents, contentType, fileDownloadName: null) { } @@ -32,7 +32,7 @@ public FileContentHttpResult(ReadOnlyMemory fileContents, string? contentT /// The bytes that represent the file contents. /// The Content-Type header of the response. /// The suggested file name. - public FileContentHttpResult( + internal FileContentHttpResult( ReadOnlyMemory fileContents, string? contentType, string? fileDownloadName) @@ -49,7 +49,7 @@ public FileContentHttpResult( /// Set to true to enable range requests processing. /// The of when the file was last modified. /// The associated with the file. - public FileContentHttpResult( + internal FileContentHttpResult( ReadOnlyMemory fileContents, string? contentType, string? fileDownloadName, diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index 030fba7befe7..2df3072a7481 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -19,7 +19,7 @@ public sealed class FileStreamHttpResult : IResult /// /// The stream with the file. /// The Content-Type of the file. - public FileStreamHttpResult(Stream fileStream, string? contentType) + internal FileStreamHttpResult(Stream fileStream, string? contentType) : this(fileStream, contentType, fileDownloadName: null) { } @@ -32,7 +32,7 @@ public FileStreamHttpResult(Stream fileStream, string? contentType) /// The stream with the file. /// The Content-Type header of the response. /// The suggested file name. - public FileStreamHttpResult( + internal FileStreamHttpResult( Stream fileStream, string? contentType, string? fileDownloadName) @@ -49,7 +49,7 @@ public FileStreamHttpResult( /// Set to true to enable range requests processing. /// The of when the file was last modified. /// The associated with the file. - public FileStreamHttpResult( + internal FileStreamHttpResult( Stream fileStream, string? contentType, string? fileDownloadName, diff --git a/src/Http/Http.Results/src/ForbidHttpResult.cs b/src/Http/Http.Results/src/ForbidHttpResult.cs index 59794d8c2949..9ac097691247 100644 --- a/src/Http/Http.Results/src/ForbidHttpResult.cs +++ b/src/Http/Http.Results/src/ForbidHttpResult.cs @@ -16,7 +16,7 @@ public sealed partial class ForbidHttpResult : IResult /// /// Initializes a new instance of . /// - public ForbidHttpResult() + internal ForbidHttpResult() : this(Array.Empty()) { } @@ -26,7 +26,7 @@ public ForbidHttpResult() /// specified authentication scheme. /// /// The authentication scheme to challenge. - public ForbidHttpResult(string authenticationScheme) + internal ForbidHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -36,7 +36,7 @@ public ForbidHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to challenge. - public ForbidHttpResult(IList authenticationSchemes) + internal ForbidHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -47,7 +47,7 @@ public ForbidHttpResult(IList authenticationSchemes) /// /// used to perform the authentication /// challenge. - public ForbidHttpResult(AuthenticationProperties? properties) + internal ForbidHttpResult(AuthenticationProperties? properties) : this(Array.Empty(), properties) { } @@ -59,7 +59,7 @@ public ForbidHttpResult(AuthenticationProperties? properties) /// The authentication schemes to challenge. /// used to perform the authentication /// challenge. - public ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) + internal ForbidHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -71,7 +71,7 @@ public ForbidHttpResult(string authenticationScheme, AuthenticationProperties? p /// The authentication scheme to challenge. /// used to perform the authentication /// challenge. - public ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + internal ForbidHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { AuthenticationSchemes = authenticationSchemes.AsReadOnly(); Properties = properties; diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index abe1788b38f9..10b86aa98965 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -15,7 +15,7 @@ public sealed class JsonHttpResult : IResult /// /// The value to format in the entity body. /// The serializer settings. - public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) + internal JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOptions) : this(value, statusCode: null, contentType: null, jsonSerializerOptions: jsonSerializerOptions) { } @@ -26,7 +26,7 @@ public JsonHttpResult(object? value, JsonSerializerOptions? jsonSerializerOption /// The value to format in the entity body. /// The HTTP status code of the response. /// The serializer settings. - public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) + internal JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jsonSerializerOptions) : this(value, statusCode: statusCode, contentType: null, jsonSerializerOptions: jsonSerializerOptions) { } @@ -37,7 +37,7 @@ public JsonHttpResult(object? value, int? statusCode, JsonSerializerOptions? jso /// The value to format in the entity body. /// The value for the Content-Type header /// The serializer settings. - public JsonHttpResult(object? value, string? contentType, JsonSerializerOptions? jsonSerializerOptions) + internal JsonHttpResult(object? value, string? contentType, JsonSerializerOptions? jsonSerializerOptions) : this(value, statusCode: null, contentType: contentType, jsonSerializerOptions: jsonSerializerOptions) { @@ -50,7 +50,7 @@ public JsonHttpResult(object? value, string? contentType, JsonSerializerOptions? /// The HTTP status code of the response. /// The serializer settings. /// The value for the Content-Type header - public JsonHttpResult(object? value, int? statusCode, string? contentType, JsonSerializerOptions? jsonSerializerOptions) + internal JsonHttpResult(object? value, int? statusCode, string? contentType, JsonSerializerOptions? jsonSerializerOptions) { Value = value; StatusCode = statusCode; diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index 957ee5b99b04..abdd396b33ad 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -15,7 +15,7 @@ public class NoContentHttpResult : IResult /// /// Initializes a new instance of the class. /// - public NoContentHttpResult() + internal NoContentHttpResult() { } diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index e0d5b04a1f0e..c8690843f92c 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -13,7 +13,7 @@ public sealed class NotFoundObjectHttpResult : IResult /// Initializes a new instance of the class with the values. /// /// The value to format in the entity body. - public NotFoundObjectHttpResult(object? value) + internal NotFoundObjectHttpResult(object? value) { Value = value; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index 54453343b070..4409d4844598 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -14,7 +14,7 @@ internal sealed class ObjectHttpResult : IResult /// Creates a new instance /// with the provided . /// - public ObjectHttpResult(object? value) + internal ObjectHttpResult(object? value) : this(value, null) { } @@ -23,7 +23,7 @@ public ObjectHttpResult(object? value) /// Creates a new instance with the provided /// , . /// - public ObjectHttpResult(object? value, int? statusCode) + internal ObjectHttpResult(object? value, int? statusCode) : this(value, statusCode, contentType: null) { } @@ -32,7 +32,7 @@ public ObjectHttpResult(object? value, int? statusCode) /// Creates a new instance with the provided /// , and . /// - public ObjectHttpResult(object? value, int? statusCode, string? contentType) + internal ObjectHttpResult(object? value, int? statusCode, string? contentType) { Value = value; diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index 62b8b22af95c..d13114a63701 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -15,7 +15,7 @@ public sealed class OkObjectHttpResult : IResult /// Initializes a new instance of the class with the values. /// /// The value to format in the entity body. - public OkObjectHttpResult(object? value) + internal OkObjectHttpResult(object? value) { Value = value; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index d7784f30a688..77b2a6e88bcd 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -17,7 +17,7 @@ public sealed partial class PhysicalFileHttpResult : IResult /// /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. - public PhysicalFileHttpResult(string fileName, string? contentType) + internal PhysicalFileHttpResult(string fileName, string? contentType) : this(fileName, contentType, fileDownloadName: null) { } @@ -30,7 +30,7 @@ public PhysicalFileHttpResult(string fileName, string? contentType) /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. /// The suggested file name. - public PhysicalFileHttpResult( + internal PhysicalFileHttpResult( string fileName, string? contentType, string? fileDownloadName) @@ -47,7 +47,7 @@ public PhysicalFileHttpResult( /// Set to true to enable range requests processing. /// The of when the file was last modified. /// The associated with the file. - public PhysicalFileHttpResult( + internal PhysicalFileHttpResult( string fileName, string? contentType, string? fileDownloadName, diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 56dba6c56ef7..997d468e482c 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -16,7 +16,7 @@ public sealed class ProblemHttpResult : IResult /// the provided . /// /// The instance to format in the entity body. - public ProblemHttpResult(ProblemDetails problemDetails) + internal ProblemHttpResult(ProblemDetails problemDetails) { ProblemDetails = problemDetails; HttpResultsHelper.ApplyProblemDetailsDefaults(ProblemDetails, statusCode: null); diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 8fd7b52f34bd..5630c56d0fe0 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -1,57 +1,39 @@ #nullable enable Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult -Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.AcceptedAtRouteHttpResult(object? routeValues, object? value) -> void -Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.AcceptedAtRouteHttpResult(string? routeName, object? routeValues, object? value) -> void Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary! Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.AcceptedAtRouteHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.AcceptedHttpResult -Microsoft.AspNetCore.Http.AcceptedHttpResult.AcceptedHttpResult(System.Uri! locationUri, object? value) -> void -Microsoft.AspNetCore.Http.AcceptedHttpResult.AcceptedHttpResult(string? location, object? value) -> void Microsoft.AspNetCore.Http.AcceptedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.AcceptedHttpResult.Location.get -> string? Microsoft.AspNetCore.Http.AcceptedHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.AcceptedHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.BadRequestObjectHttpResult -Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.BadRequestObjectHttpResult(object? error) -> void Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.BadRequestObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.ChallengeHttpResult Microsoft.AspNetCore.Http.ChallengeHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! -Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult() -> void -Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void -Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(System.Collections.Generic.IList! authenticationSchemes) -> void -Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(System.Collections.Generic.IList! authenticationSchemes, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void -Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(string! authenticationScheme) -> void -Microsoft.AspNetCore.Http.ChallengeHttpResult.ChallengeHttpResult(string! authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.ChallengeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ChallengeHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? Microsoft.AspNetCore.Http.ConflictObjectHttpResult -Microsoft.AspNetCore.Http.ConflictObjectHttpResult.ConflictObjectHttpResult(object? error) -> void Microsoft.AspNetCore.Http.ConflictObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ConflictObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.ConflictObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.ContentHttpResult Microsoft.AspNetCore.Http.ContentHttpResult.Content.get -> string? -Microsoft.AspNetCore.Http.ContentHttpResult.ContentHttpResult(string? content, string? contentType) -> void -Microsoft.AspNetCore.Http.ContentHttpResult.ContentHttpResult(string? content, string? contentType, int? statusCode) -> void Microsoft.AspNetCore.Http.ContentHttpResult.ContentType.get -> string? Microsoft.AspNetCore.Http.ContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ContentHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult -Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.CreatedAtRouteHttpResult(object? routeValues, object? value) -> void -Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.CreatedAtRouteHttpResult(string? routeName, object? routeValues, object? value) -> void Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.CreatedAtRouteHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.CreatedHttpResult -Microsoft.AspNetCore.Http.CreatedHttpResult.CreatedHttpResult(System.Uri! locationUri, object? value) -> void -Microsoft.AspNetCore.Http.CreatedHttpResult.CreatedHttpResult(string! location, object? value) -> void Microsoft.AspNetCore.Http.CreatedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.CreatedHttpResult.Location.get -> string? Microsoft.AspNetCore.Http.CreatedHttpResult.StatusCode.get -> int @@ -63,9 +45,6 @@ Microsoft.AspNetCore.Http.FileContentHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.FileContentHttpResult.EnableRangeProcessing.get -> bool Microsoft.AspNetCore.Http.FileContentHttpResult.EntityTag.get -> Microsoft.Net.Http.Headers.EntityTagHeaderValue? Microsoft.AspNetCore.Http.FileContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.FileContentHttpResult.FileContentHttpResult(System.ReadOnlyMemory fileContents, string? contentType) -> void -Microsoft.AspNetCore.Http.FileContentHttpResult.FileContentHttpResult(System.ReadOnlyMemory fileContents, string? contentType, string? fileDownloadName) -> void -Microsoft.AspNetCore.Http.FileContentHttpResult.FileContentHttpResult(System.ReadOnlyMemory fileContents, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.FileContentHttpResult.FileContents.get -> System.ReadOnlyMemory Microsoft.AspNetCore.Http.FileContentHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.FileContentHttpResult.FileLength.get -> long? @@ -78,42 +57,26 @@ Microsoft.AspNetCore.Http.FileStreamHttpResult.ExecuteAsync(Microsoft.AspNetCore Microsoft.AspNetCore.Http.FileStreamHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.FileStreamHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStream.get -> System.IO.Stream! -Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStreamHttpResult(System.IO.Stream! fileStream, string? contentType) -> void -Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStreamHttpResult(System.IO.Stream! fileStream, string? contentType, string? fileDownloadName) -> void -Microsoft.AspNetCore.Http.FileStreamHttpResult.FileStreamHttpResult(System.IO.Stream! fileStream, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.FileStreamHttpResult.LastModified.get -> System.DateTimeOffset? Microsoft.AspNetCore.Http.ForbidHttpResult Microsoft.AspNetCore.Http.ForbidHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! Microsoft.AspNetCore.Http.ForbidHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult() -> void -Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void -Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(System.Collections.Generic.IList! authenticationSchemes) -> void -Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(System.Collections.Generic.IList! authenticationSchemes, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void -Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(string! authenticationScheme) -> void -Microsoft.AspNetCore.Http.ForbidHttpResult.ForbidHttpResult(string! authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.ForbidHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? Microsoft.AspNetCore.Http.JsonHttpResult Microsoft.AspNetCore.Http.JsonHttpResult.ContentType.get -> string? Microsoft.AspNetCore.Http.JsonHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void -Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, int? statusCode, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void -Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, int? statusCode, string? contentType, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void -Microsoft.AspNetCore.Http.JsonHttpResult.JsonHttpResult(object? value, string? contentType, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions) -> void Microsoft.AspNetCore.Http.JsonHttpResult.JsonSerializerOptions.get -> System.Text.Json.JsonSerializerOptions? Microsoft.AspNetCore.Http.JsonHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.JsonHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.NoContentHttpResult Microsoft.AspNetCore.Http.NoContentHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.NoContentHttpResult.NoContentHttpResult() -> void Microsoft.AspNetCore.Http.NoContentHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.NotFoundObjectHttpResult Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.NotFoundObjectHttpResult(object? value) -> void Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.NotFoundObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.OkObjectHttpResult Microsoft.AspNetCore.Http.OkObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! -Microsoft.AspNetCore.Http.OkObjectHttpResult.OkObjectHttpResult(object? value) -> void Microsoft.AspNetCore.Http.OkObjectHttpResult.StatusCode.get -> int Microsoft.AspNetCore.Http.OkObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.PhysicalFileHttpResult @@ -125,14 +88,10 @@ Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.PhysicalFileHttpResult.FileName.get -> string! Microsoft.AspNetCore.Http.PhysicalFileHttpResult.LastModified.get -> System.DateTimeOffset? -Microsoft.AspNetCore.Http.PhysicalFileHttpResult.PhysicalFileHttpResult(string! fileName, string? contentType) -> void -Microsoft.AspNetCore.Http.PhysicalFileHttpResult.PhysicalFileHttpResult(string! fileName, string? contentType, string? fileDownloadName) -> void -Microsoft.AspNetCore.Http.PhysicalFileHttpResult.PhysicalFileHttpResult(string! fileName, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.ProblemHttpResult Microsoft.AspNetCore.Http.ProblemHttpResult.ContentType.get -> string! Microsoft.AspNetCore.Http.ProblemHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemDetails.get -> Microsoft.AspNetCore.Mvc.ProblemDetails! -Microsoft.AspNetCore.Http.ProblemHttpResult.ProblemHttpResult(Microsoft.AspNetCore.Mvc.ProblemDetails! problemDetails) -> void Microsoft.AspNetCore.Http.ProblemHttpResult.StatusCode.get -> int? Microsoft.AspNetCore.Http.PushStreamHttpResult Microsoft.AspNetCore.Http.PushStreamHttpResult.ContentType.get -> string! @@ -142,31 +101,17 @@ Microsoft.AspNetCore.Http.PushStreamHttpResult.ExecuteAsync(Microsoft.AspNetCore Microsoft.AspNetCore.Http.PushStreamHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.PushStreamHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.PushStreamHttpResult.LastModified.get -> System.DateTimeOffset? -Microsoft.AspNetCore.Http.PushStreamHttpResult.PushStreamHttpResult(System.Func! streamWriterCallback, string? contentType) -> void -Microsoft.AspNetCore.Http.PushStreamHttpResult.PushStreamHttpResult(System.Func! streamWriterCallback, string? contentType, string? fileDownloadName) -> void -Microsoft.AspNetCore.Http.PushStreamHttpResult.PushStreamHttpResult(System.Func! streamWriterCallback, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void Microsoft.AspNetCore.Http.RedirectHttpResult Microsoft.AspNetCore.Http.RedirectHttpResult.AcceptLocalUrlOnly.get -> bool Microsoft.AspNetCore.Http.RedirectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.RedirectHttpResult.Permanent.get -> bool Microsoft.AspNetCore.Http.RedirectHttpResult.PreserveMethod.get -> bool -Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url) -> void -Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url, bool acceptLocalUrlOnly, bool permanent, bool preserveMethod) -> void -Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url, bool permanent) -> void -Microsoft.AspNetCore.Http.RedirectHttpResult.RedirectHttpResult(string! url, bool permanent, bool preserveMethod) -> void Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Fragment.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Permanent.get -> bool Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.PreserveMethod.get -> bool -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(object? routeValues) -> void -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues) -> void -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent) -> void -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent, bool preserveMethod) -> void -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent, bool preserveMethod, string? fragment) -> void -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, bool permanent, string? fragment) -> void -Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RedirectToRouteHttpResult(string? routeName, object? routeValues, string? fragment) -> void Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteName.get -> string? Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? Microsoft.AspNetCore.Http.SignInHttpResult @@ -174,30 +119,19 @@ Microsoft.AspNetCore.Http.SignInHttpResult.AuthenticationScheme.get -> string? Microsoft.AspNetCore.Http.SignInHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.SignInHttpResult.Principal.get -> System.Security.Claims.ClaimsPrincipal! Microsoft.AspNetCore.Http.SignInHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.SignInHttpResult.SignInHttpResult(System.Security.Claims.ClaimsPrincipal! principal) -> void -Microsoft.AspNetCore.Http.SignInHttpResult.SignInHttpResult(System.Security.Claims.ClaimsPrincipal! principal, string? authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.SignOutHttpResult Microsoft.AspNetCore.Http.SignOutHttpResult.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! Microsoft.AspNetCore.Http.SignOutHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.SignOutHttpResult.Properties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties? -Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult() -> void -Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(Microsoft.AspNetCore.Authentication.AuthenticationProperties! properties) -> void -Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(System.Collections.Generic.IList! authenticationSchemes) -> void -Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(System.Collections.Generic.IList! authenticationSchemes, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void -Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(string! authenticationScheme) -> void -Microsoft.AspNetCore.Http.SignOutHttpResult.SignOutHttpResult(string! authenticationScheme, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties) -> void Microsoft.AspNetCore.Http.StatusCodeHttpResult Microsoft.AspNetCore.Http.StatusCodeHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCode.get -> int -Microsoft.AspNetCore.Http.StatusCodeHttpResult.StatusCodeHttpResult(int statusCode) -> void Microsoft.AspNetCore.Http.UnauthorizedHttpResult Microsoft.AspNetCore.Http.UnauthorizedHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.UnauthorizedHttpResult.StatusCode.get -> int -Microsoft.AspNetCore.Http.UnauthorizedHttpResult.UnauthorizedHttpResult() -> void Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.StatusCode.get -> int -Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.UnprocessableEntityObjectHttpResult(object? error) -> void Microsoft.AspNetCore.Http.UnprocessableEntityObjectHttpResult.Value.get -> object? Microsoft.AspNetCore.Http.VirtualFileHttpResult Microsoft.AspNetCore.Http.VirtualFileHttpResult.ContentType.get -> string! @@ -208,11 +142,8 @@ Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileDownloadName.get -> string? Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileLength.get -> long? Microsoft.AspNetCore.Http.VirtualFileHttpResult.FileName.get -> string! Microsoft.AspNetCore.Http.VirtualFileHttpResult.LastModified.get -> System.DateTimeOffset? -Microsoft.AspNetCore.Http.VirtualFileHttpResult.VirtualFileHttpResult(string! fileName, string? contentType) -> void -Microsoft.AspNetCore.Http.VirtualFileHttpResult.VirtualFileHttpResult(string! fileName, string? contentType, string? fileDownloadName) -> void -Microsoft.AspNetCore.Http.VirtualFileHttpResult.VirtualFileHttpResult(string! fileName, string? contentType, string? fileDownloadName, bool enableRangeProcessing, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> void +static Microsoft.AspNetCore.Http.EmptyHttpResult.Instance.get -> Microsoft.AspNetCore.Http.EmptyHttpResult! static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, bool enableRangeProcessing = false, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Empty.get -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.Func! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Stream(System.IO.Pipelines.PipeReader! pipeReader, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null, bool enableRangeProcessing = false) -> Microsoft.AspNetCore.Http.IResult! -static readonly Microsoft.AspNetCore.Http.EmptyHttpResult.Instance -> Microsoft.AspNetCore.Http.EmptyHttpResult! diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index 0671d042f1ab..669b0fde6964 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -19,7 +19,7 @@ public sealed class PushStreamHttpResult : IResult /// /// The stream writer callback. /// The Content-Type header of the response. - public PushStreamHttpResult(Func streamWriterCallback, string? contentType) + internal PushStreamHttpResult(Func streamWriterCallback, string? contentType) : this(streamWriterCallback, contentType, fileDownloadName: null) { } @@ -32,7 +32,7 @@ public PushStreamHttpResult(Func streamWriterCallback, string? con /// The stream writer callback. /// The Content-Type header of the response. /// The suggested file name. - public PushStreamHttpResult( + internal PushStreamHttpResult( Func streamWriterCallback, string? contentType, string? fileDownloadName) @@ -49,7 +49,7 @@ public PushStreamHttpResult( /// Set to true to enable range requests processing. /// The of when the file was last modified. /// The associated with the file. - public PushStreamHttpResult( + internal PushStreamHttpResult( Func streamWriterCallback, string? contentType, string? fileDownloadName, diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index 127b0a39eda9..97f6050dc93e 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -20,7 +20,7 @@ public sealed partial class RedirectHttpResult : IResult /// provided. /// /// The URL to redirect to. - public RedirectHttpResult(string url) + internal RedirectHttpResult(string url) : this(url, permanent: false) { } @@ -31,7 +31,7 @@ public RedirectHttpResult(string url) /// /// The URL to redirect to. /// Specifies whether the redirect should be permanent (301) or temporary (302). - public RedirectHttpResult(string url, bool permanent) + internal RedirectHttpResult(string url, bool permanent) : this(url, permanent, preserveMethod: false) { } @@ -44,7 +44,7 @@ public RedirectHttpResult(string url, bool permanent) /// Specifies whether the redirect should be permanent (301) or temporary (302). /// If set to true, make the temporary redirect (307) /// or permanent redirect (308) preserve the initial request method. - public RedirectHttpResult(string url, bool permanent, bool preserveMethod) + internal RedirectHttpResult(string url, bool permanent, bool preserveMethod) : this(url, acceptLocalUrlOnly: false, permanent, preserveMethod) { } @@ -58,7 +58,7 @@ public RedirectHttpResult(string url, bool permanent, bool preserveMethod) /// or permanent redirect (308) preserve the initial request method. /// If set to true, only local URLs are accepted /// and will throw an exception when the supplied URL is not considered local. - public RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, bool preserveMethod) + internal RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, bool preserveMethod) { if (url == null) { diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index ae6b4148b8da..a50b2e182d3e 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -19,7 +19,7 @@ public sealed partial class RedirectToRouteHttpResult : IResult /// provided. /// /// The parameters for the route. - public RedirectToRouteHttpResult(object? routeValues) + internal RedirectToRouteHttpResult(object? routeValues) : this(routeName: null, routeValues: routeValues) { } @@ -30,7 +30,7 @@ public RedirectToRouteHttpResult(object? routeValues) /// /// The name of the route. /// The parameters for the route. - public RedirectToRouteHttpResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues) : this(routeName, routeValues, permanent: false) @@ -45,7 +45,7 @@ public RedirectToRouteHttpResult( /// The parameters for the route. /// If set to true, makes the redirect permanent (301). /// Otherwise a temporary redirect is used (302). - public RedirectToRouteHttpResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent) @@ -63,7 +63,7 @@ public RedirectToRouteHttpResult( /// Otherwise a temporary redirect is used (302). /// If set to true, make the temporary redirect (307) /// or permanent redirect (308) preserve the initial request method. - public RedirectToRouteHttpResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -79,7 +79,7 @@ public RedirectToRouteHttpResult( /// The name of the route. /// The parameters for the route. /// The fragment to add to the URL. - public RedirectToRouteHttpResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, string? fragment) @@ -96,7 +96,7 @@ public RedirectToRouteHttpResult( /// If set to true, makes the redirect permanent (301). /// Otherwise a temporary redirect is used (302). /// The fragment to add to the URL. - public RedirectToRouteHttpResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, @@ -116,7 +116,7 @@ public RedirectToRouteHttpResult( /// If set to true, make the temporary redirect (307) /// or permanent redirect (308) preserve the initial request method. /// The fragment to add to the URL. - public RedirectToRouteHttpResult( + internal RedirectToRouteHttpResult( string? routeName, object? routeValues, bool permanent, diff --git a/src/Http/Http.Results/src/SignInHttpResult.cs b/src/Http/Http.Results/src/SignInHttpResult.cs index d96b54c42220..d94c1167fa09 100644 --- a/src/Http/Http.Results/src/SignInHttpResult.cs +++ b/src/Http/Http.Results/src/SignInHttpResult.cs @@ -18,7 +18,7 @@ public sealed partial class SignInHttpResult : IResult /// default authentication scheme. /// /// The claims principal containing the user claims. - public SignInHttpResult(ClaimsPrincipal principal) + internal SignInHttpResult(ClaimsPrincipal principal) : this(principal, authenticationScheme: null, properties: null) { } @@ -30,7 +30,7 @@ public SignInHttpResult(ClaimsPrincipal principal) /// The claims principal containing the user claims. /// The authentication schemes to use when signing in the user. /// used to perform the sign-in operation. - public SignInHttpResult(ClaimsPrincipal principal, string? authenticationScheme, AuthenticationProperties? properties) + internal SignInHttpResult(ClaimsPrincipal principal, string? authenticationScheme, AuthenticationProperties? properties) { Principal = principal ?? throw new ArgumentNullException(nameof(principal)); AuthenticationScheme = authenticationScheme; diff --git a/src/Http/Http.Results/src/SignOutHttpResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs index 7780663e559e..b3acdbe9d376 100644 --- a/src/Http/Http.Results/src/SignOutHttpResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -16,7 +16,7 @@ public sealed partial class SignOutHttpResult : IResult /// /// Initializes a new instance of with the default sign out scheme. /// - public SignOutHttpResult() + internal SignOutHttpResult() : this(Array.Empty()) { } @@ -26,7 +26,7 @@ public SignOutHttpResult() /// specified authentication scheme and . /// /// used to perform the sign-out operation. - public SignOutHttpResult(AuthenticationProperties properties) + internal SignOutHttpResult(AuthenticationProperties properties) : this(Array.Empty(), properties) { } @@ -36,7 +36,7 @@ public SignOutHttpResult(AuthenticationProperties properties) /// specified authentication scheme. /// /// The authentication scheme to use when signing out the user. - public SignOutHttpResult(string authenticationScheme) + internal SignOutHttpResult(string authenticationScheme) : this(new[] { authenticationScheme }) { } @@ -46,7 +46,7 @@ public SignOutHttpResult(string authenticationScheme) /// specified authentication schemes. /// /// The authentication schemes to use when signing out the user. - public SignOutHttpResult(IList authenticationSchemes) + internal SignOutHttpResult(IList authenticationSchemes) : this(authenticationSchemes, properties: null) { } @@ -57,7 +57,7 @@ public SignOutHttpResult(IList authenticationSchemes) /// /// The authentication schemes to use when signing out the user. /// used to perform the sign-out operation. - public SignOutHttpResult(string authenticationScheme, AuthenticationProperties? properties) + internal SignOutHttpResult(string authenticationScheme, AuthenticationProperties? properties) : this(new[] { authenticationScheme }, properties) { } @@ -68,7 +68,7 @@ public SignOutHttpResult(string authenticationScheme, AuthenticationProperties? /// /// The authentication scheme to use when signing out the user. /// used to perform the sign-out operation. - public SignOutHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) + internal SignOutHttpResult(IList authenticationSchemes, AuthenticationProperties? properties) { if (authenticationSchemes is null) { diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index 0af2deced38c..77b94b60d914 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -17,7 +17,7 @@ public sealed partial class StatusCodeHttpResult : IResult /// with the given . /// /// The HTTP status code of the response. - public StatusCodeHttpResult(int statusCode) + internal StatusCodeHttpResult(int statusCode) { StatusCode = statusCode; } diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index dc28aa822130..05bc0de6eef2 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -15,7 +15,7 @@ public sealed class UnauthorizedHttpResult : IResult /// /// Initializes a new instance of the class. /// - public UnauthorizedHttpResult() + internal UnauthorizedHttpResult() { } diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index a4cdd51d646a..1ca6ae689c97 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -14,7 +14,7 @@ public sealed class UnprocessableEntityObjectHttpResult : IResult /// provided. /// /// The error content to format in the entity body. - public UnprocessableEntityObjectHttpResult(object? error) + internal UnprocessableEntityObjectHttpResult(object? error) { Value = error; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index a6958c71f330..310e6b00cdc1 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -24,7 +24,7 @@ public sealed class VirtualFileHttpResult : IResult /// /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. - public VirtualFileHttpResult(string fileName, string? contentType) + internal VirtualFileHttpResult(string fileName, string? contentType) : this(fileName, contentType, fileDownloadName: null) { } @@ -37,7 +37,7 @@ public VirtualFileHttpResult(string fileName, string? contentType) /// The path to the file. The path must be an absolute path. /// The Content-Type header of the response. /// The suggested file name. - public VirtualFileHttpResult( + internal VirtualFileHttpResult( string fileName, string? contentType, string? fileDownloadName) @@ -54,7 +54,7 @@ public VirtualFileHttpResult( /// Set to true to enable range requests processing. /// The of when the file was last modified. /// The associated with the file. - public VirtualFileHttpResult( + internal VirtualFileHttpResult( string fileName, string? contentType, string? fileDownloadName, diff --git a/src/Http/Http.Results/test/NoContentResultTests.cs b/src/Http/Http.Results/test/NoContentResultTests.cs index 55cdffb5ad09..18b56774b6f5 100644 --- a/src/Http/Http.Results/test/NoContentResultTests.cs +++ b/src/Http/Http.Results/test/NoContentResultTests.cs @@ -19,7 +19,6 @@ public void NoContentResultTests_InitializesStatusCode() Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); } - [Fact] public void NoContentResultTests_ExecuteResultSetsResponseStatusCode() { From 00877b51023a5406e65df27e60431e49598081a5 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 14:52:50 -0700 Subject: [PATCH 24/29] Create logger using string category --- .../src/AcceptedAtRouteHttpResult.cs | 7 ++++++- .../Http.Results/src/AcceptedHttpResult.cs | 6 ++++++ .../src/BadRequestObjectHttpResult.cs | 15 ++++++++++++++- .../Http.Results/src/ChallengeHttpResult.cs | 4 +++- .../src/ConflictObjectHttpResult.cs | 15 ++++++++++++++- .../Http.Results/src/ContentHttpResult.cs | 11 ++++++++++- .../src/CreatedAtRouteHttpResult.cs | 6 ++++++ .../Http.Results/src/CreatedHttpResult.cs | 7 +++++++ .../Http.Results/src/FileContentHttpResult.cs | 13 +++++++++++-- .../Http.Results/src/FileStreamHttpResult.cs | 7 +++++++ src/Http/Http.Results/src/ForbidHttpResult.cs | 4 +++- .../Http.Results/src/HttpResultsHelper.cs | 19 ++++++------------- src/Http/Http.Results/src/JsonHttpResult.cs | 11 ++++++++++- .../Http.Results/src/NoContentHttpResult.cs | 5 ++++- .../src/NotFoundObjectHttpResult.cs | 15 ++++++++++++++- src/Http/Http.Results/src/ObjectHttpResult.cs | 15 ++++++++++++++- .../Http.Results/src/OkObjectHttpResult.cs | 14 +++++++++++++- .../src/PhysicalFileHttpResult.cs | 7 +++++++ .../Http.Results/src/ProblemHttpResult.cs | 15 ++++++++++----- .../Http.Results/src/PushStreamHttpResult.cs | 13 +++++++++++-- .../Http.Results/src/RedirectHttpResult.cs | 5 ++++- .../src/RedirectToRouteHttpResult.cs | 4 +++- src/Http/Http.Results/src/SignInHttpResult.cs | 4 +++- .../Http.Results/src/SignOutHttpResult.cs | 4 +++- .../Http.Results/src/StatusCodeHttpResult.cs | 4 +++- .../src/UnauthorizedHttpResult.cs | 4 +++- .../UnprocessableEntityObjectHttpResult.cs | 15 ++++++++++++++- .../Http.Results/src/VirtualFileHttpResult.cs | 6 ++++++ 28 files changed, 215 insertions(+), 40 deletions(-) diff --git a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs index 118b0fe4a1f3..0e45e84164ce 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteHttpResult.cs @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; /// /// An that on execution will write an object to the response @@ -78,7 +79,11 @@ public Task ExecuteAsync(HttpContext httpContext) throw new InvalidOperationException("No route matches the supplied values."); } + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.AcceptedAtRouteResult"); + httpContext.Response.Headers.Location = url; - return HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + return HttpResultsHelper.WriteResultAsJsonAsync(httpContext, logger, Value, StatusCode); } } diff --git a/src/Http/Http.Results/src/AcceptedHttpResult.cs b/src/Http/Http.Results/src/AcceptedHttpResult.cs index de8f27b9dc43..d0b6026e0c56 100644 --- a/src/Http/Http.Results/src/AcceptedHttpResult.cs +++ b/src/Http/Http.Results/src/AcceptedHttpResult.cs @@ -4,6 +4,8 @@ namespace Microsoft.AspNetCore.Http; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; /// /// An that on execution will write an object to the response @@ -74,8 +76,12 @@ public Task ExecuteAsync(HttpContext httpContext) httpContext.Response.Headers.Location = Location; } + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.AcceptedResult"); return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, + logger, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs index 0f1ed6f5077c..dc9ad6e4fb2f 100644 --- a/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs +++ b/src/Http/Http.Results/src/BadRequestObjectHttpResult.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// An that on execution will write an object to the response /// with Bad Request (400) status code. @@ -32,5 +35,15 @@ internal BadRequestObjectHttpResult(object? error) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.BadRequestObjectResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: logger, + Value, + StatusCode); + } } diff --git a/src/Http/Http.Results/src/ChallengeHttpResult.cs b/src/Http/Http.Results/src/ChallengeHttpResult.cs index 34b45d14d4b6..cff6c06937dd 100644 --- a/src/Http/Http.Results/src/ChallengeHttpResult.cs +++ b/src/Http/Http.Results/src/ChallengeHttpResult.cs @@ -90,7 +90,9 @@ internal ChallengeHttpResult(IList authenticationSchemes, Authentication /// public async Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.ChallengeResult"); Log.ChallengeResultExecuting(logger, AuthenticationSchemes); diff --git a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs index 2a94739033e6..782775395bfc 100644 --- a/src/Http/Http.Results/src/ConflictObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ConflictObjectHttpResult.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// An that on execution will write an object to the response /// with Conflict (409) status code. @@ -32,5 +35,15 @@ internal ConflictObjectHttpResult(object? error) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.ConflictObjectResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: logger, + Value, + StatusCode); + } } diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index b9da81036665..65fe3cc99b3d 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// An that when executed /// will produce a response with content. @@ -53,5 +56,11 @@ internal ContentHttpResult(string? content, string? contentType, int? statusCode /// The for the current request. /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsContentAsync(httpContext, Content, StatusCode, ContentType); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.ContentResult"); + + return HttpResultsHelper.WriteResultAsContentAsync(httpContext, logger, Content, StatusCode, ContentType); + } } diff --git a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs index 52c24ae4065d..f59593be8505 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteHttpResult.cs @@ -5,6 +5,7 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; /// /// An that on execution will write an object to the response @@ -77,9 +78,14 @@ public Task ExecuteAsync(HttpContext httpContext) throw new InvalidOperationException("No route matches the supplied values."); } + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.CreatedAtRouteResult"); + httpContext.Response.Headers.Location = url; return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, + logger, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/CreatedHttpResult.cs b/src/Http/Http.Results/src/CreatedHttpResult.cs index cb1aea18e534..4106cacd7fec 100644 --- a/src/Http/Http.Results/src/CreatedHttpResult.cs +++ b/src/Http/Http.Results/src/CreatedHttpResult.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// An that on execution will write an object to the response /// with status code Created (201) and Location header. @@ -69,8 +72,12 @@ public Task ExecuteAsync(HttpContext httpContext) httpContext.Response.Headers.Location = Location; } + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.CreatedResult"); return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, + logger, Value, StatusCode); } diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index ec52667f6d77..cc069bdd1108 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Internal; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -102,9 +104,15 @@ internal FileContentHttpResult( public ReadOnlyMemory FileContents { get; internal init; } /// - public Task ExecuteAsync(HttpContext httpContext) => - HttpResultsHelper.WriteResultAsFileAsync( + public Task ExecuteAsync(HttpContext httpContext) + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.FileContentResult"); + + return HttpResultsHelper.WriteResultAsFileAsync( httpContext, + logger, FileDownloadName, FileLength, ContentType, @@ -112,4 +120,5 @@ public Task ExecuteAsync(HttpContext httpContext) => LastModified, EntityTag, (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength)); + } } diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index 2df3072a7481..d5447ee2fb20 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Internal; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -113,10 +115,15 @@ internal FileStreamHttpResult( /// public async Task ExecuteAsync(HttpContext httpContext) { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.FileStreamResult"); + await using (FileStream) { await HttpResultsHelper.WriteResultAsFileAsync( httpContext, + logger, FileDownloadName, FileLength, ContentType, diff --git a/src/Http/Http.Results/src/ForbidHttpResult.cs b/src/Http/Http.Results/src/ForbidHttpResult.cs index 9ac097691247..f9d8d9c27897 100644 --- a/src/Http/Http.Results/src/ForbidHttpResult.cs +++ b/src/Http/Http.Results/src/ForbidHttpResult.cs @@ -90,7 +90,9 @@ internal ForbidHttpResult(IList authenticationSchemes, AuthenticationPro /// public async Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.ForbidResult"); Log.ForbidResultExecuting(logger, AuthenticationSchemes); diff --git a/src/Http/Http.Results/src/HttpResultsHelper.cs b/src/Http/Http.Results/src/HttpResultsHelper.cs index 03c300e1a40e..8bf3d34126a9 100644 --- a/src/Http/Http.Results/src/HttpResultsHelper.cs +++ b/src/Http/Http.Results/src/HttpResultsHelper.cs @@ -1,17 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http; - using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +namespace Microsoft.AspNetCore.Http; internal static partial class HttpResultsHelper { private const string DefaultContentType = "text/plain; charset=utf-8"; @@ -19,12 +17,13 @@ internal static partial class HttpResultsHelper public static Task WriteResultAsJsonAsync( HttpContext httpContext, + ILogger logger, object? value, int? statusCode, string? contentType = null, JsonSerializerOptions? jsonSerializerOptions = null) { - Log.WritingResultAsJson(GetLogger(httpContext), value, statusCode); + Log.WritingResultAsJson(logger, value, statusCode); if (statusCode is { } code) { @@ -45,6 +44,7 @@ public static Task WriteResultAsJsonAsync( public static async Task WriteResultAsContentAsync( HttpContext httpContext, + ILogger logger, string? content, int? statusCode, string? contentType = null) @@ -65,7 +65,7 @@ public static async Task WriteResultAsContentAsync( response.StatusCode = code; } - Log.WritingResultAsContent(GetLogger(httpContext), resolvedContentType); + Log.WritingResultAsContent(logger, resolvedContentType); if (content != null) { @@ -76,6 +76,7 @@ public static async Task WriteResultAsContentAsync( public static Task WriteResultAsFileAsync( HttpContext httpContext, + ILogger logger, string? fileDownloadName, long? fileLength, string contentType, @@ -84,7 +85,6 @@ public static Task WriteResultAsFileAsync( EntityTagHeaderValue? entityTag, Func writeOperation) { - var logger = GetLogger(httpContext); fileDownloadName ??= string.Empty; Log.WritingResultAsFile(logger, fileDownloadName); @@ -159,13 +159,6 @@ public static void ApplyProblemDetailsDefaults(ProblemDetails problemDetails, in } } - private static ILogger GetLogger(HttpContext httpContext) - { - var factory = httpContext.RequestServices.GetRequiredService(); - var logger = factory.CreateLogger(typeof(HttpResultsHelper)); - return logger; - } - internal static partial class Log { public static void WritingResultAsJson(ILogger logger, object? value, int? statusCode) diff --git a/src/Http/Http.Results/src/JsonHttpResult.cs b/src/Http/Http.Results/src/JsonHttpResult.cs index 10b86aa98965..1e7800a32bdb 100644 --- a/src/Http/Http.Results/src/JsonHttpResult.cs +++ b/src/Http/Http.Results/src/JsonHttpResult.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Http; @@ -81,10 +83,17 @@ internal JsonHttpResult(object? value, int? statusCode, string? contentType, Jso /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync( + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.JsonResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, + logger, Value, StatusCode, ContentType, JsonSerializerOptions); + } } diff --git a/src/Http/Http.Results/src/NoContentHttpResult.cs b/src/Http/Http.Results/src/NoContentHttpResult.cs index abdd396b33ad..0339cc3f1f38 100644 --- a/src/Http/Http.Results/src/NoContentHttpResult.cs +++ b/src/Http/Http.Results/src/NoContentHttpResult.cs @@ -27,7 +27,10 @@ internal NoContentHttpResult() /// public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.NoContentResult"); + HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode); httpContext.Response.StatusCode = StatusCode; diff --git a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs index c8690843f92c..45220b7d0483 100644 --- a/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs +++ b/src/Http/Http.Results/src/NotFoundObjectHttpResult.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// An that on execution will write an object to the response /// with Not Found (404) status code. @@ -31,5 +34,15 @@ internal NotFoundObjectHttpResult(object? value) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.NotFoundObjectResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: logger, + Value, + StatusCode); + } } diff --git a/src/Http/Http.Results/src/ObjectHttpResult.cs b/src/Http/Http.Results/src/ObjectHttpResult.cs index 4409d4844598..252ab122a1da 100644 --- a/src/Http/Http.Results/src/ObjectHttpResult.cs +++ b/src/Http/Http.Results/src/ObjectHttpResult.cs @@ -4,6 +4,8 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; /// /// An that on execution will write an object to the response. @@ -63,5 +65,16 @@ internal ObjectHttpResult(object? value, int? statusCode, string? contentType) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode, ContentType); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.ObjectResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: logger, + Value, + StatusCode, + ContentType); + } } diff --git a/src/Http/Http.Results/src/OkObjectHttpResult.cs b/src/Http/Http.Results/src/OkObjectHttpResult.cs index d13114a63701..2dedc139d7ab 100644 --- a/src/Http/Http.Results/src/OkObjectHttpResult.cs +++ b/src/Http/Http.Results/src/OkObjectHttpResult.cs @@ -4,6 +4,8 @@ namespace Microsoft.AspNetCore.Http; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; /// /// An that on execution will write an object to the response @@ -33,5 +35,15 @@ internal OkObjectHttpResult(object? value) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.OkObjectResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: logger, + Value, + StatusCode); + } } diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index 77b2a6e88bcd..30ba394b3b41 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -114,8 +116,13 @@ public Task ExecuteAsync(HttpContext httpContext) LastModified ??= fileInfo.LastWriteTimeUtc; FileLength = fileInfo.Length; + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.PhysicalFileResult"); + return HttpResultsHelper.WriteResultAsFileAsync( httpContext, + logger, FileDownloadName, FileLength, ContentType, diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 997d468e482c..262f5c5b20c0 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -4,6 +4,8 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; /// /// An that on execution will write Problem Details @@ -39,9 +41,12 @@ internal ProblemHttpResult(ProblemDetails problemDetails) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync( - httpContext, - value: ProblemDetails, - StatusCode, - ContentType); + { + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: httpContext.RequestServices.GetRequiredService>(), + value: ProblemDetails, + StatusCode, + ContentType); + } } diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index 669b0fde6964..b3b61d2453ab 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -96,9 +98,15 @@ internal PushStreamHttpResult( public long? FileLength { get; internal set; } /// - public Task ExecuteAsync(HttpContext httpContext) => - HttpResultsHelper.WriteResultAsFileAsync( + public Task ExecuteAsync(HttpContext httpContext) + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.PushStreamResult"); + + return HttpResultsHelper.WriteResultAsFileAsync( httpContext, + logger, FileDownloadName, FileLength, ContentType, @@ -106,4 +114,5 @@ public Task ExecuteAsync(HttpContext httpContext) => LastModified, EntityTag, (context, _, _) => _streamWriterCallback(context.Response.Body)); + } } diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index 97f6050dc93e..f078507d6dfa 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -100,7 +100,10 @@ internal RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, /// public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.RedirectResult"); + var isLocalUrl = SharedUrlHelper.IsLocalUrl(Url); if (AcceptLocalUrlOnly && !isLocalUrl) diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index a50b2e182d3e..586694792e59 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -171,7 +171,9 @@ public Task ExecuteAsync(HttpContext httpContext) throw new InvalidOperationException("No route matches the supplied values."); } - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.RedirectToRouteResult"); Log.RedirectToRouteResultExecuting(logger, destinationUrl, RouteName); if (PreserveMethod) diff --git a/src/Http/Http.Results/src/SignInHttpResult.cs b/src/Http/Http.Results/src/SignInHttpResult.cs index d94c1167fa09..a1b0d805e867 100644 --- a/src/Http/Http.Results/src/SignInHttpResult.cs +++ b/src/Http/Http.Results/src/SignInHttpResult.cs @@ -55,7 +55,9 @@ internal SignInHttpResult(ClaimsPrincipal principal, string? authenticationSchem /// public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.SignInResult"); Log.SignInResultExecuting(logger, AuthenticationScheme, Principal); diff --git a/src/Http/Http.Results/src/SignOutHttpResult.cs b/src/Http/Http.Results/src/SignOutHttpResult.cs index b3acdbe9d376..7d187fe52c15 100644 --- a/src/Http/Http.Results/src/SignOutHttpResult.cs +++ b/src/Http/Http.Results/src/SignOutHttpResult.cs @@ -92,7 +92,9 @@ internal SignOutHttpResult(IList authenticationSchemes, AuthenticationPr /// public async Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.SignOutResult"); Log.SignOutResultExecuting(logger, AuthenticationSchemes); diff --git a/src/Http/Http.Results/src/StatusCodeHttpResult.cs b/src/Http/Http.Results/src/StatusCodeHttpResult.cs index 77b94b60d914..3e24b45a7767 100644 --- a/src/Http/Http.Results/src/StatusCodeHttpResult.cs +++ b/src/Http/Http.Results/src/StatusCodeHttpResult.cs @@ -34,7 +34,9 @@ internal StatusCodeHttpResult(int statusCode) /// A task that represents the asynchronous execute operation. public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.StatusCodeResult"); HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode); httpContext.Response.StatusCode = StatusCode; diff --git a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs index 05bc0de6eef2..79bc2c17dd91 100644 --- a/src/Http/Http.Results/src/UnauthorizedHttpResult.cs +++ b/src/Http/Http.Results/src/UnauthorizedHttpResult.cs @@ -27,7 +27,9 @@ internal UnauthorizedHttpResult() /// public Task ExecuteAsync(HttpContext httpContext) { - var logger = httpContext.RequestServices.GetRequiredService>(); + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.UnauthorizedResult"); HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode); httpContext.Response.StatusCode = StatusCode; diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index 1ca6ae689c97..6e0e3a2ac6bc 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -3,6 +3,9 @@ namespace Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + /// /// An that on execution will write an object to the response /// with Unprocessable Entity (422) status code. @@ -28,5 +31,15 @@ internal UnprocessableEntityObjectHttpResult(object? error) /// public Task ExecuteAsync(HttpContext httpContext) - => HttpResultsHelper.WriteResultAsJsonAsync(httpContext, Value, StatusCode); + { + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.UnprocessableEntityObjectResult"); + + return HttpResultsHelper.WriteResultAsJsonAsync( + httpContext, + logger: logger, + Value, + StatusCode); + } } diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index 310e6b00cdc1..116d4913228b 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -113,8 +114,13 @@ public Task ExecuteAsync(HttpContext httpContext) LastModified = LastModified ?? fileInfo.LastModified; FileLength = fileInfo.Length; + // Creating the logger with a string to preserve the category after the refactoring. + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.VirtualFileResult"); + return HttpResultsHelper.WriteResultAsFileAsync( httpContext, + logger, FileDownloadName, FileLength, ContentType, From 825f4c0cbb323763a1205fb6fd1e0825bf6e07fb Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 14:53:11 -0700 Subject: [PATCH 25/29] Adding more unittests --- .../test/BadRequestObjectResultTests.cs | 51 +++++++++++++++++++ .../Http.Results/test/ChallengeResultTest.cs | 1 + .../test/ConflictObjectResultTest.cs | 50 ++++++++++++++++++ .../Http.Results/test/ForbidResultTest.cs | 1 + .../test/LocalRedirectResultTest.cs | 1 + .../Http.Results/test/OkObjectResultTest.cs | 23 +++++++++ .../Http.Results/test/ProblemResultTests.cs | 1 + .../test/RedirectToRouteResultTest.cs | 1 + .../Http.Results/test/SignInResultTest.cs | 1 + .../Http.Results/test/SignOutResultTest.cs | 1 + .../UnprocessableEntityObjectResultTests.cs | 51 +++++++++++++++++++ 11 files changed, 182 insertions(+) diff --git a/src/Http/Http.Results/test/BadRequestObjectResultTests.cs b/src/Http/Http.Results/test/BadRequestObjectResultTests.cs index dea5e8aa94ac..c72a82752af1 100644 --- a/src/Http/Http.Results/test/BadRequestObjectResultTests.cs +++ b/src/Http/Http.Results/test/BadRequestObjectResultTests.cs @@ -3,6 +3,11 @@ namespace Microsoft.AspNetCore.Http.Result; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + public class BadRequestObjectResultTests { [Fact] @@ -29,4 +34,50 @@ public void BadRequestObjectResult_ProblemDetails_SetsStatusCodeAndValue() Assert.Equal(StatusCodes.Status400BadRequest, obj.Status); Assert.Equal(obj, result.Value); } + + [Fact] + public async Task BadRequestObjectResult_ExecuteAsync_SetsStatusCode() + { + // Arrange + var result = new BadRequestObjectHttpResult("Hello"); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, httpContext.Response.StatusCode); + } + + [Fact] + public async Task BadRequestObjectResult_ExecuteResultAsync_FormatsData() + { + // Arrange + var result = new BadRequestObjectHttpResult("Hello"); + var stream = new MemoryStream(); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + Response = + { + Body = stream, + }, + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal("\"Hello\"", Encoding.UTF8.GetString(stream.ToArray())); + } + + private static IServiceProvider CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + return services.BuildServiceProvider(); + } } diff --git a/src/Http/Http.Results/test/ChallengeResultTest.cs b/src/Http/Http.Results/test/ChallengeResultTest.cs index 6cdc8dfff372..95817e6ba449 100644 --- a/src/Http/Http.Results/test/ChallengeResultTest.cs +++ b/src/Http/Http.Results/test/ChallengeResultTest.cs @@ -54,6 +54,7 @@ private static IServiceCollection CreateServices() { var services = new ServiceCollection(); services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); + services.AddSingleton(NullLoggerFactory.Instance); return services; } } diff --git a/src/Http/Http.Results/test/ConflictObjectResultTest.cs b/src/Http/Http.Results/test/ConflictObjectResultTest.cs index 3472a3e07b29..091d03bc605f 100644 --- a/src/Http/Http.Results/test/ConflictObjectResultTest.cs +++ b/src/Http/Http.Results/test/ConflictObjectResultTest.cs @@ -3,7 +3,11 @@ namespace Microsoft.AspNetCore.Http.Result; +using System.Text; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; public class ConflictObjectResultTest { @@ -31,4 +35,50 @@ public void ConflictObjectResult_ProblemDetails_SetsStatusCodeAndValue() Assert.Equal(StatusCodes.Status409Conflict, obj.Status); Assert.Equal(obj, conflictObjectResult.Value); } + + [Fact] + public async Task ConflictObjectResult_ExecuteAsync_SetsStatusCode() + { + // Arrange + var result = new ConflictObjectHttpResult("Hello"); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status409Conflict, httpContext.Response.StatusCode); + } + + [Fact] + public async Task ConflictObjectResult_ExecuteResultAsync_FormatsData() + { + // Arrange + var result = new ConflictObjectHttpResult("Hello"); + var stream = new MemoryStream(); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + Response = + { + Body = stream, + }, + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal("\"Hello\"", Encoding.UTF8.GetString(stream.ToArray())); + } + + private static IServiceProvider CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + return services.BuildServiceProvider(); + } } diff --git a/src/Http/Http.Results/test/ForbidResultTest.cs b/src/Http/Http.Results/test/ForbidResultTest.cs index 3da4f07dbadc..d3b770178ded 100644 --- a/src/Http/Http.Results/test/ForbidResultTest.cs +++ b/src/Http/Http.Results/test/ForbidResultTest.cs @@ -121,6 +121,7 @@ private static IServiceCollection CreateServices() { var services = new ServiceCollection(); services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); + services.AddSingleton(NullLoggerFactory.Instance); return services; } } diff --git a/src/Http/Http.Results/test/LocalRedirectResultTest.cs b/src/Http/Http.Results/test/LocalRedirectResultTest.cs index abd58a3cbec1..33e49752fefd 100644 --- a/src/Http/Http.Results/test/LocalRedirectResultTest.cs +++ b/src/Http/Http.Results/test/LocalRedirectResultTest.cs @@ -121,6 +121,7 @@ private static IServiceProvider GetServiceProvider() { var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(typeof(ILogger<>), typeof(NullLogger<>)); + serviceCollection.AddSingleton(NullLoggerFactory.Instance); return serviceCollection.BuildServiceProvider(); } diff --git a/src/Http/Http.Results/test/OkObjectResultTest.cs b/src/Http/Http.Results/test/OkObjectResultTest.cs index bb19a01aecca..64d4d85a8a82 100644 --- a/src/Http/Http.Results/test/OkObjectResultTest.cs +++ b/src/Http/Http.Results/test/OkObjectResultTest.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -36,6 +37,28 @@ public void OkObjectResult_ProblemDetails_SetsStatusCodeAndValue() Assert.Equal(obj, result.Value); } + [Fact] + public async Task OkObjectResult_ExecuteAsync_FormatsData() + { + // Arrange + var result = new OkObjectHttpResult("Hello"); + var stream = new MemoryStream(); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + Response = + { + Body = stream, + }, + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal("\"Hello\"", Encoding.UTF8.GetString(stream.ToArray())); + } + private static HttpContext GetHttpContext() { var httpContext = new DefaultHttpContext(); diff --git a/src/Http/Http.Results/test/ProblemResultTests.cs b/src/Http/Http.Results/test/ProblemResultTests.cs index 349445009ed1..28dfd0653cd8 100644 --- a/src/Http/Http.Results/test/ProblemResultTests.cs +++ b/src/Http/Http.Results/test/ProblemResultTests.cs @@ -94,6 +94,7 @@ public async Task ExecuteAsync_GetsStatusCodeFromProblemDetails() private static IServiceProvider CreateServices() { var services = new ServiceCollection(); + services.AddTransient(typeof(ILogger<>), typeof(NullLogger<>)); services.AddSingleton(NullLoggerFactory.Instance); return services.BuildServiceProvider(); diff --git a/src/Http/Http.Results/test/RedirectToRouteResultTest.cs b/src/Http/Http.Results/test/RedirectToRouteResultTest.cs index e92a875924a0..99dcac7836dc 100644 --- a/src/Http/Http.Results/test/RedirectToRouteResultTest.cs +++ b/src/Http/Http.Results/test/RedirectToRouteResultTest.cs @@ -100,6 +100,7 @@ private static IServiceCollection CreateServices(string path) services.AddSingleton(new TestLinkGenerator { Url = path }); services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); + services.AddSingleton(NullLoggerFactory.Instance); return services; } } diff --git a/src/Http/Http.Results/test/SignInResultTest.cs b/src/Http/Http.Results/test/SignInResultTest.cs index 38d33edba4ee..6aef97223c54 100644 --- a/src/Http/Http.Results/test/SignInResultTest.cs +++ b/src/Http/Http.Results/test/SignInResultTest.cs @@ -86,6 +86,7 @@ private static DefaultHttpContext GetHttpContext(IAuthenticationService auth) private static IServiceCollection CreateServices() { var services = new ServiceCollection(); + services.AddSingleton(NullLoggerFactory.Instance); services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); return services; } diff --git a/src/Http/Http.Results/test/SignOutResultTest.cs b/src/Http/Http.Results/test/SignOutResultTest.cs index 3c2682976d7a..d3cfa5073659 100644 --- a/src/Http/Http.Results/test/SignOutResultTest.cs +++ b/src/Http/Http.Results/test/SignOutResultTest.cs @@ -85,6 +85,7 @@ private static DefaultHttpContext GetHttpContext(IAuthenticationService auth) private static IServiceCollection CreateServices() { var services = new ServiceCollection(); + services.AddSingleton(NullLoggerFactory.Instance); services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)); return services; } diff --git a/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs b/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs index ffc95adfeb62..f04410925e29 100644 --- a/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs +++ b/src/Http/Http.Results/test/UnprocessableEntityObjectResultTests.cs @@ -3,6 +3,11 @@ namespace Microsoft.AspNetCore.Http.Result; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + public class UnprocessableEntityObjectResultTests { [Fact] @@ -29,4 +34,50 @@ public void UnprocessableEntityObjectResult_SetsStatusCodeAndValue() Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); Assert.Equal(obj, result.Value); } + + [Fact] + public async Task UnprocessableEntityObjectResult_ExecuteAsync_SetsStatusCode() + { + // Arrange + var result = new UnprocessableEntityObjectHttpResult("Hello"); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal(StatusCodes.Status422UnprocessableEntity, httpContext.Response.StatusCode); + } + + [Fact] + public async Task UnprocessableEntityObjectResult_ExecuteResultAsync_FormatsData() + { + // Arrange + var result = new UnprocessableEntityObjectHttpResult("Hello"); + var stream = new MemoryStream(); + var httpContext = new DefaultHttpContext() + { + RequestServices = CreateServices(), + Response = + { + Body = stream, + }, + }; + + // Act + await result.ExecuteAsync(httpContext); + + // Assert + Assert.Equal("\"Hello\"", Encoding.UTF8.GetString(stream.ToArray())); + } + + private static IServiceProvider CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + return services.BuildServiceProvider(); + } } From 73800b9eda6d3b473d7664718e3c715ed2f545a7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 18 Mar 2022 15:40:03 -0700 Subject: [PATCH 26/29] Update src/Http/Http.Results/src/EmptyHttpResult.cs Co-authored-by: Brennan --- src/Http/Http.Results/src/EmptyHttpResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.Results/src/EmptyHttpResult.cs b/src/Http/Http.Results/src/EmptyHttpResult.cs index 30754c832cca..9e9b25c166dd 100644 --- a/src/Http/Http.Results/src/EmptyHttpResult.cs +++ b/src/Http/Http.Results/src/EmptyHttpResult.cs @@ -16,7 +16,7 @@ private EmptyHttpResult() /// /// Gets an instance of . /// - public static EmptyHttpResult Instance => new(); + public static EmptyHttpResult Instance { get; } = new(); /// public Task ExecuteAsync(HttpContext httpContext) From 8fb15dd37e7c38f7346e93756a3bc7e10785cc41 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 18 Mar 2022 15:41:28 -0700 Subject: [PATCH 27/29] Update ContentHttpResult.cs --- src/Http/Http.Results/src/ContentHttpResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.Results/src/ContentHttpResult.cs b/src/Http/Http.Results/src/ContentHttpResult.cs index 65fe3cc99b3d..a50675408e2d 100644 --- a/src/Http/Http.Results/src/ContentHttpResult.cs +++ b/src/Http/Http.Results/src/ContentHttpResult.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; /// -/// An that when executed +/// An that when executed /// will produce a response with content. /// public sealed partial class ContentHttpResult : IResult From dac87115894a2105f2a5b01440cfdd5caa9d0f00 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 16:49:20 -0700 Subject: [PATCH 28/29] avoid closure allocation --- .../Http.Results/src/FileContentHttpResult.cs | 9 ++++--- .../Http.Results/src/FileStreamHttpResult.cs | 10 ++++--- .../Http.Results/src/HttpResultsHelper.cs | 26 ++++++++++--------- .../src/PhysicalFileHttpResult.cs | 17 +++++++----- .../Http.Results/src/PublicAPI.Unshipped.txt | 2 +- .../Http.Results/src/PushStreamHttpResult.cs | 9 ++++--- .../Http.Results/src/RedirectHttpResult.cs | 9 +++---- .../UnprocessableEntityObjectHttpResult.cs | 8 +++--- .../Http.Results/src/VirtualFileHttpResult.cs | 16 ++++++------ 9 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/Http/Http.Results/src/FileContentHttpResult.cs b/src/Http/Http.Results/src/FileContentHttpResult.cs index cc069bdd1108..56cd17d13944 100644 --- a/src/Http/Http.Results/src/FileContentHttpResult.cs +++ b/src/Http/Http.Results/src/FileContentHttpResult.cs @@ -110,7 +110,7 @@ public Task ExecuteAsync(HttpContext httpContext) var loggerFactory = httpContext.RequestServices.GetRequiredService(); var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.FileContentResult"); - return HttpResultsHelper.WriteResultAsFileAsync( + var (range, rangeLength, completed) = HttpResultsHelper.WriteResultAsFileCore( httpContext, logger, FileDownloadName, @@ -118,7 +118,10 @@ public Task ExecuteAsync(HttpContext httpContext) ContentType, EnableRangeProcessing, LastModified, - EntityTag, - (context, range, rangeLength) => FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength)); + EntityTag); + + return completed ? + Task.CompletedTask : + FileResultHelper.WriteFileAsync(httpContext, FileContents, range, rangeLength); } } diff --git a/src/Http/Http.Results/src/FileStreamHttpResult.cs b/src/Http/Http.Results/src/FileStreamHttpResult.cs index d5447ee2fb20..4a2273c535f0 100644 --- a/src/Http/Http.Results/src/FileStreamHttpResult.cs +++ b/src/Http/Http.Results/src/FileStreamHttpResult.cs @@ -121,7 +121,7 @@ public async Task ExecuteAsync(HttpContext httpContext) await using (FileStream) { - await HttpResultsHelper.WriteResultAsFileAsync( + var (range, rangeLength, completed) = HttpResultsHelper.WriteResultAsFileCore( httpContext, logger, FileDownloadName, @@ -129,8 +129,12 @@ await HttpResultsHelper.WriteResultAsFileAsync( ContentType, EnableRangeProcessing, LastModified, - EntityTag, - (context, range, rangeLength) => FileResultHelper.WriteFileAsync(context, FileStream, range, rangeLength)); + EntityTag); + + if (!completed) + { + await FileResultHelper.WriteFileAsync(httpContext, FileStream, range, rangeLength); + } } } } diff --git a/src/Http/Http.Results/src/HttpResultsHelper.cs b/src/Http/Http.Results/src/HttpResultsHelper.cs index 8bf3d34126a9..f447e1d9539e 100644 --- a/src/Http/Http.Results/src/HttpResultsHelper.cs +++ b/src/Http/Http.Results/src/HttpResultsHelper.cs @@ -42,7 +42,7 @@ public static Task WriteResultAsJsonAsync( contentType: contentType); } - public static async Task WriteResultAsContentAsync( + public static Task WriteResultAsContentAsync( HttpContext httpContext, ILogger logger, string? content, @@ -70,11 +70,13 @@ public static async Task WriteResultAsContentAsync( if (content != null) { response.ContentLength = resolvedContentTypeEncoding.GetByteCount(content); - await response.WriteAsync(content, resolvedContentTypeEncoding); + return response.WriteAsync(content, resolvedContentTypeEncoding); } + + return Task.CompletedTask; } - public static Task WriteResultAsFileAsync( + public static (RangeItemHeaderValue? range, long rangeLength, bool completed) WriteResultAsFileCore( HttpContext httpContext, ILogger logger, string? fileDownloadName, @@ -82,9 +84,9 @@ public static Task WriteResultAsFileAsync( string contentType, bool enableRangeProcessing, DateTimeOffset? lastModified, - EntityTagHeaderValue? entityTag, - Func writeOperation) + EntityTagHeaderValue? entityTag) { + var completed = false; fileDownloadName ??= string.Empty; Log.WritingResultAsFile(logger, fileDownloadName); @@ -107,22 +109,22 @@ public static Task WriteResultAsFileAsync( entityTag, logger); - if (!serveBody) + if (range != null) { - return Task.CompletedTask; + FileResultHelper.Log.WritingRangeToBody(logger); } - if (range != null && rangeLength == 0) + if (!serveBody) { - return Task.CompletedTask; + completed = true; } - if (range != null) + if (range != null && rangeLength == 0) { - FileResultHelper.Log.WritingRangeToBody(logger); + completed = true; } - return writeOperation(httpContext, range, rangeLength); + return (range, rangeLength, completed); } public static void ApplyProblemDetailsDefaultsIfNeeded(object? value, int? statusCode) diff --git a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs index 30ba394b3b41..97f8e01dca38 100644 --- a/src/Http/Http.Results/src/PhysicalFileHttpResult.cs +++ b/src/Http/Http.Results/src/PhysicalFileHttpResult.cs @@ -120,7 +120,7 @@ public Task ExecuteAsync(HttpContext httpContext) var loggerFactory = httpContext.RequestServices.GetRequiredService(); var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.PhysicalFileResult"); - return HttpResultsHelper.WriteResultAsFileAsync( + var (range, rangeLength, completed) = HttpResultsHelper.WriteResultAsFileCore( httpContext, logger, FileDownloadName, @@ -128,16 +128,19 @@ public Task ExecuteAsync(HttpContext httpContext) ContentType, EnableRangeProcessing, LastModified, - EntityTag, - writeOperation: ExecuteCoreAsync); + EntityTag); + + return completed ? + Task.CompletedTask : + ExecuteCoreAsync(httpContext, range, rangeLength, FileName); } - private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + private static Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength, string fileName) { var response = httpContext.Response; - if (!Path.IsPathRooted(FileName)) + if (!Path.IsPathRooted(fileName)) { - throw new NotSupportedException($"Path '{FileName}' was not rooted."); + throw new NotSupportedException($"Path '{fileName}' was not rooted."); } var offset = 0L; @@ -149,7 +152,7 @@ private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? ran } return response.SendFileAsync( - FileName, + fileName, offset: offset, count: count); } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 5630c56d0fe0..b53f85370ac8 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -106,7 +106,7 @@ Microsoft.AspNetCore.Http.RedirectHttpResult.AcceptLocalUrlOnly.get -> bool Microsoft.AspNetCore.Http.RedirectHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.RedirectHttpResult.Permanent.get -> bool Microsoft.AspNetCore.Http.RedirectHttpResult.PreserveMethod.get -> bool -Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string? +Microsoft.AspNetCore.Http.RedirectHttpResult.Url.get -> string! Microsoft.AspNetCore.Http.RedirectToRouteHttpResult Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.RedirectToRouteHttpResult.Fragment.get -> string? diff --git a/src/Http/Http.Results/src/PushStreamHttpResult.cs b/src/Http/Http.Results/src/PushStreamHttpResult.cs index b3b61d2453ab..71f454af2bf2 100644 --- a/src/Http/Http.Results/src/PushStreamHttpResult.cs +++ b/src/Http/Http.Results/src/PushStreamHttpResult.cs @@ -104,7 +104,7 @@ public Task ExecuteAsync(HttpContext httpContext) var loggerFactory = httpContext.RequestServices.GetRequiredService(); var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.PushStreamResult"); - return HttpResultsHelper.WriteResultAsFileAsync( + var (range, rangeLength, completed) = HttpResultsHelper.WriteResultAsFileCore( httpContext, logger, FileDownloadName, @@ -112,7 +112,10 @@ public Task ExecuteAsync(HttpContext httpContext) ContentType, EnableRangeProcessing, LastModified, - EntityTag, - (context, _, _) => _streamWriterCallback(context.Response.Body)); + EntityTag); + + return completed ? + Task.CompletedTask : + _streamWriterCallback(httpContext.Response.Body); } } diff --git a/src/Http/Http.Results/src/RedirectHttpResult.cs b/src/Http/Http.Results/src/RedirectHttpResult.cs index f078507d6dfa..3c1bdc6ce911 100644 --- a/src/Http/Http.Results/src/RedirectHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectHttpResult.cs @@ -13,8 +13,6 @@ namespace Microsoft.AspNetCore.Http; /// public sealed partial class RedirectHttpResult : IResult { - private readonly string _url; - /// /// Initializes a new instance of the class with the values /// provided. @@ -70,11 +68,10 @@ internal RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, throw new ArgumentException("Argument cannot be null or empty", nameof(url)); } + Url = url; Permanent = permanent; PreserveMethod = preserveMethod; AcceptLocalUrlOnly = acceptLocalUrlOnly; - - _url = url; } /// @@ -90,7 +87,7 @@ internal RedirectHttpResult(string url, bool acceptLocalUrlOnly, bool permanent, /// /// Gets the URL to redirect to. /// - public string? Url => _url; + public string Url { get; } /// /// Gets an indication that only local URLs are accepted. @@ -112,7 +109,7 @@ public Task ExecuteAsync(HttpContext httpContext) } // IsLocalUrl is called to handle URLs starting with '~/'. - var destinationUrl = isLocalUrl ? SharedUrlHelper.Content(httpContext, contentPath: _url) : _url; + var destinationUrl = isLocalUrl ? SharedUrlHelper.Content(httpContext, contentPath: Url) : Url; Log.RedirectResultExecuting(logger, destinationUrl); diff --git a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs index 6e0e3a2ac6bc..d1c8eda62dae 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityObjectHttpResult.cs @@ -13,13 +13,13 @@ namespace Microsoft.AspNetCore.Http; public sealed class UnprocessableEntityObjectHttpResult : IResult { /// - /// Initializes a new instance of the class with the values + /// Initializes a new instance of the class with the values /// provided. /// - /// The error content to format in the entity body. - internal UnprocessableEntityObjectHttpResult(object? error) + /// The value to format in the entity body. + internal UnprocessableEntityObjectHttpResult(object? value) { - Value = error; + Value = value; HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } diff --git a/src/Http/Http.Results/src/VirtualFileHttpResult.cs b/src/Http/Http.Results/src/VirtualFileHttpResult.cs index 116d4913228b..cac64665d23d 100644 --- a/src/Http/Http.Results/src/VirtualFileHttpResult.cs +++ b/src/Http/Http.Results/src/VirtualFileHttpResult.cs @@ -17,7 +17,6 @@ namespace Microsoft.AspNetCore.Http; public sealed class VirtualFileHttpResult : IResult { private string _fileName; - private IFileInfo? _fileInfo; /// /// Creates a new instance with @@ -109,8 +108,6 @@ public Task ExecuteAsync(HttpContext httpContext) { throw new FileNotFoundException($"Could not find file: {FileName}.", FileName); } - - _fileInfo = fileInfo; LastModified = LastModified ?? fileInfo.LastModified; FileLength = fileInfo.Length; @@ -118,7 +115,7 @@ public Task ExecuteAsync(HttpContext httpContext) var loggerFactory = httpContext.RequestServices.GetRequiredService(); var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Result.VirtualFileResult"); - return HttpResultsHelper.WriteResultAsFileAsync( + var (range, rangeLength, completed) = HttpResultsHelper.WriteResultAsFileCore( httpContext, logger, FileDownloadName, @@ -126,11 +123,14 @@ public Task ExecuteAsync(HttpContext httpContext) ContentType, EnableRangeProcessing, LastModified, - EntityTag, - ExecuteCoreAsync); + EntityTag); + + return completed ? + Task.CompletedTask : + ExecuteCoreAsync(httpContext, range, rangeLength, fileInfo); } - private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength) + private static Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? range, long rangeLength, IFileInfo fileInfo) { var response = httpContext.Response; var offset = 0L; @@ -142,7 +142,7 @@ private Task ExecuteCoreAsync(HttpContext httpContext, RangeItemHeaderValue? ran } return response.SendFileAsync( - _fileInfo!, + fileInfo!, offset, count); } From 1d76b6ee9f17218db114f761a505217f75f8ea92 Mon Sep 17 00:00:00 2001 From: Bruno Lins de Oliveira Date: Fri, 18 Mar 2022 18:45:18 -0700 Subject: [PATCH 29/29] unit loggerfactory --- src/Http/Http.Results/src/ProblemHttpResult.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 262f5c5b20c0..0a5c52a0fcca 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -42,9 +42,12 @@ internal ProblemHttpResult(ProblemDetails problemDetails) /// public Task ExecuteAsync(HttpContext httpContext) { + var loggerFactory = httpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger(typeof(ProblemHttpResult)); + return HttpResultsHelper.WriteResultAsJsonAsync( httpContext, - logger: httpContext.RequestServices.GetRequiredService>(), + logger, value: ProblemDetails, StatusCode, ContentType);