From 0e3d59c400f233ce0bbbeaf7cc82547d8d548306 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 12 Feb 2023 10:21:52 +0800 Subject: [PATCH] [AOT] Add Http.Results RouteValueDictionary overloads --- src/Http/Http.Results/src/AcceptedAtRoute.cs | 4 +- .../Http.Results/src/AcceptedAtRouteOfT.cs | 4 +- src/Http/Http.Results/src/CreatedAtRoute.cs | 4 +- .../Http.Results/src/CreatedAtRouteOfT.cs | 4 +- .../Http.Results/src/PublicAPI.Unshipped.txt | 12 ++ .../src/RedirectToRouteHttpResult.cs | 6 +- src/Http/Http.Results/src/Results.cs | 108 +++++++--- src/Http/Http.Results/src/TypedResults.cs | 74 +++++++ src/Http/Http.Results/test/ResultsTests.cs | 193 +++++++++++++++++- .../Http.Results/test/TypedResultsTests.cs | 149 +++++++++++++- 10 files changed, 512 insertions(+), 46 deletions(-) diff --git a/src/Http/Http.Results/src/AcceptedAtRoute.cs b/src/Http/Http.Results/src/AcceptedAtRoute.cs index cdc91cfcd4dd..8ab753f83acc 100644 --- a/src/Http/Http.Results/src/AcceptedAtRoute.cs +++ b/src/Http/Http.Results/src/AcceptedAtRoute.cs @@ -50,10 +50,10 @@ internal AcceptedAtRoute(string? routeName, object? routeValues) /// The route data to use for generating the URL. internal AcceptedAtRoute( string? routeName, - RouteValueDictionary routeValues) + RouteValueDictionary? routeValues) { RouteName = routeName; - RouteValues = routeValues; + RouteValues = routeValues ?? new RouteValueDictionary(); } /// diff --git a/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs b/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs index dc45eba65954..1d681bdf8438 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs @@ -54,12 +54,12 @@ internal AcceptedAtRoute(string? routeName, object? routeValues, TValue? value) /// The value to format in the entity body. internal AcceptedAtRoute( string? routeName, - RouteValueDictionary routeValues, + RouteValueDictionary? routeValues, TValue? value) { Value = value; RouteName = routeName; - RouteValues = routeValues; + RouteValues = routeValues ?? new RouteValueDictionary(); HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } diff --git a/src/Http/Http.Results/src/CreatedAtRoute.cs b/src/Http/Http.Results/src/CreatedAtRoute.cs index 9948e811de16..aeb115b5b30b 100644 --- a/src/Http/Http.Results/src/CreatedAtRoute.cs +++ b/src/Http/Http.Results/src/CreatedAtRoute.cs @@ -50,10 +50,10 @@ internal CreatedAtRoute(string? routeName, object? routeValues) /// The route data to use for generating the URL. internal CreatedAtRoute( string? routeName, - RouteValueDictionary routeValues) + RouteValueDictionary? routeValues) { RouteName = routeName; - RouteValues = routeValues; + RouteValues = routeValues ?? new RouteValueDictionary(); } /// diff --git a/src/Http/Http.Results/src/CreatedAtRouteOfT.cs b/src/Http/Http.Results/src/CreatedAtRouteOfT.cs index f5b026979c5f..3f674d615c3f 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteOfT.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteOfT.cs @@ -54,12 +54,12 @@ internal CreatedAtRoute(string? routeName, object? routeValues, TValue? value) /// The value to format in the entity body. internal CreatedAtRoute( string? routeName, - RouteValueDictionary routeValues, + RouteValueDictionary? routeValues, TValue? value) { Value = value; RouteName = routeName; - RouteValues = routeValues; + RouteValues = routeValues ?? new RouteValueDictionary(); HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode); } diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 393991164c2b..c3143932a894 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -4,7 +4,11 @@ *REMOVED*static Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.Instance.get -> Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult! Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions) Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions) +Microsoft.AspNetCore.Http.HttpResults.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary! +*REMOVED*Microsoft.AspNetCore.Http.HttpResults.RedirectToRouteHttpResult.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary? static Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.Instance.get -> Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult! (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions) +static Microsoft.AspNetCore.Http.Results.AcceptedAtRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues, object? value = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.AcceptedAtRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues, TValue? value = default(TValue?)) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Created() -> Microsoft.AspNetCore.Http.IResult! *REMOVED*static Microsoft.AspNetCore.Http.Results.Created(string! uri, object? value) -> Microsoft.AspNetCore.Http.IResult! *REMOVED*static Microsoft.AspNetCore.Http.Results.Created(System.Uri! uri, object? value) -> Microsoft.AspNetCore.Http.IResult! @@ -12,10 +16,15 @@ static Microsoft.AspNetCore.Http.Results.Created(string? uri, object? value) -> static Microsoft.AspNetCore.Http.Results.Created(System.Uri? uri, object? value) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Created(string? uri, TValue? value) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Created(System.Uri? uri, TValue? value) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.CreatedAtRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues, object? value = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.CreatedAtRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues, TValue? value = default(TValue?)) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Json(object? data, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, string? contentType = null, int? statusCode = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Json(object? data, System.Type! type, System.Text.Json.Serialization.JsonSerializerContext! context, string? contentType = null, int? statusCode = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Json(TValue? data, System.Text.Json.Serialization.JsonSerializerContext! context, string? contentType = null, int? statusCode = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Json(TValue? data, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, string? contentType = null, int? statusCode = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.RedirectToRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues, bool permanent = false, bool preserveMethod = false, string? fragment = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.TypedResults.AcceptedAtRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues) -> Microsoft.AspNetCore.Http.HttpResults.AcceptedAtRoute! +static Microsoft.AspNetCore.Http.TypedResults.AcceptedAtRoute(TValue? value, string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues) -> Microsoft.AspNetCore.Http.HttpResults.AcceptedAtRoute! static Microsoft.AspNetCore.Http.TypedResults.Created() -> Microsoft.AspNetCore.Http.HttpResults.Created! static Microsoft.AspNetCore.Http.TypedResults.Created(string? uri) -> Microsoft.AspNetCore.Http.HttpResults.Created! static Microsoft.AspNetCore.Http.TypedResults.Created(System.Uri? uri) -> Microsoft.AspNetCore.Http.HttpResults.Created! @@ -27,5 +36,8 @@ static Microsoft.AspNetCore.Http.TypedResults.Created(System.Uri? uri, T *REMOVED*static Microsoft.AspNetCore.Http.TypedResults.Created(string! uri) -> Microsoft.AspNetCore.Http.HttpResults.Created! *REMOVED*static Microsoft.AspNetCore.Http.TypedResults.Created(System.Uri! uri, TValue? value) -> Microsoft.AspNetCore.Http.HttpResults.Created! *REMOVED*static Microsoft.AspNetCore.Http.TypedResults.Created(string! uri, TValue? value) -> Microsoft.AspNetCore.Http.HttpResults.Created! +static Microsoft.AspNetCore.Http.TypedResults.CreatedAtRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues) -> Microsoft.AspNetCore.Http.HttpResults.CreatedAtRoute! +static Microsoft.AspNetCore.Http.TypedResults.CreatedAtRoute(TValue? value, string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues) -> Microsoft.AspNetCore.Http.HttpResults.CreatedAtRoute! static Microsoft.AspNetCore.Http.TypedResults.Json(TValue? data, System.Text.Json.Serialization.JsonSerializerContext! context, string? contentType = null, int? statusCode = null) -> Microsoft.AspNetCore.Http.HttpResults.JsonHttpResult! static Microsoft.AspNetCore.Http.TypedResults.Json(TValue? data, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, string? contentType = null, int? statusCode = null) -> Microsoft.AspNetCore.Http.HttpResults.JsonHttpResult! +static Microsoft.AspNetCore.Http.TypedResults.RedirectToRoute(string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? routeValues, bool permanent = false, bool preserveMethod = false, string? fragment = null) -> Microsoft.AspNetCore.Http.HttpResults.RedirectToRouteHttpResult! diff --git a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs index bd823fc9c13b..2e491f3151a1 100644 --- a/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs +++ b/src/Http/Http.Results/src/RedirectToRouteHttpResult.cs @@ -96,7 +96,7 @@ internal RedirectToRouteHttpResult( bool preserveMethod, string? fragment) : this( routeName, - routeValues == null ? null : new RouteValueDictionary(routeValues), + new RouteValueDictionary(routeValues), permanent, preserveMethod, fragment) @@ -122,7 +122,7 @@ internal RedirectToRouteHttpResult( string? fragment) { RouteName = routeName; - RouteValues = routeValues; + RouteValues = routeValues ?? new RouteValueDictionary(); PreserveMethod = preserveMethod; Permanent = permanent; Fragment = fragment; @@ -136,7 +136,7 @@ internal RedirectToRouteHttpResult( /// /// Gets the route data to use for generating the URL. /// - public RouteValueDictionary? RouteValues { get; } + public RouteValueDictionary RouteValues { get; } /// /// Gets the value that specifies that the redirect should be permanent if true or temporary if false. diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index ab760c99f429..20ab2f6a2297 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -146,9 +147,7 @@ public static IResult Text(string? content, string? contentType, Encoding? conte /// If encoding is provided by both the 'charset' and the parameters, then /// the parameter is chosen as the final encoding. /// -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult Text(string? content, string? contentType = null, Encoding? contentEncoding = null, int? statusCode = null) -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters => TypedResults.Text(content, contentType, contentEncoding, statusCode); /// @@ -158,9 +157,7 @@ public static IResult Text(string? content, string? contentType = null, Encoding /// The content type (MIME type). /// The status code to return. /// The created object for the response. -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads public static IResult Text(ReadOnlySpan utf8Content, string? contentType = null, int? statusCode = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads => TypedResults.Text(utf8Content, contentType, statusCode); /// @@ -241,7 +238,6 @@ public static IResult Json(object? data, Type type, JsonSerializerContext contex /// recreating cached data with each call. [RequiresUnreferencedCode(JsonHttpResultTrimmerWarning.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonHttpResultTrimmerWarning.SerializationRequiresDynamicCodeMessage)] -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult Json(TValue? data, JsonSerializerOptions? options = null, string? contentType = null, int? statusCode = null) #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters => TypedResults.Json(data, options, contentType, statusCode); @@ -294,9 +290,7 @@ public static IResult Json(TValue? data, JsonSerializerContext context, /// 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( -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters byte[] fileContents, string? contentType = null, string? fileDownloadName = null, @@ -344,9 +338,7 @@ public static IResult Bytes( /// 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 Bytes( -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters ReadOnlyMemory contents, string? contentType = null, string? fileDownloadName = null, @@ -377,9 +369,7 @@ public static IResult Bytes( /// /// The parameter is disposed after the response is sent. /// -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult File( -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters Stream fileStream, string? contentType = null, string? fileDownloadName = null, @@ -438,9 +428,7 @@ public static IResult Stream( /// /// The parameter is completed after the response is sent. /// -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult Stream( -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters PipeReader pipeReader, string? contentType = null, string? fileDownloadName = null, @@ -464,9 +452,7 @@ public static IResult Stream( /// The to be configure the ETag response header /// and perform conditional requests. /// The created for the response. -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult Stream( -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters Func streamWriterCallback, string? contentType = null, string? fileDownloadName = null, @@ -488,9 +474,7 @@ public static IResult Stream( /// The associated with the file. /// Set to true to enable range requests processing. /// The created for the response. -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult File( -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters string path, string? contentType = null, string? fileDownloadName = null, @@ -576,6 +560,34 @@ public static IResult LocalRedirect([StringSyntax(StringSyntaxAttribute.Uri, Uri public static IResult RedirectToRoute(string? routeName = null, object? routeValues = null, bool permanent = false, bool preserveMethod = false, string? fragment = null) => TypedResults.RedirectToRoute(routeName, routeValues, permanent, preserveMethod, fragment); + /// + /// Redirects to the specified route. + /// + /// + /// When and are set, sets the status code. + /// + /// + /// When is set, sets the status code. + /// + /// + /// When is set, sets the status code. + /// + /// + /// Otherwise, configures . + /// + /// + /// + /// The name of the route. + /// The parameters for a route. + /// 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. + /// The fragment to add to the URL. + /// The created for the response. +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public static IResult RedirectToRoute(string? routeName, RouteValueDictionary? routeValues, bool permanent = false, bool preserveMethod = false, string? fragment = null) +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters + => TypedResults.RedirectToRoute(routeName, routeValues, permanent, preserveMethod, fragment); + /// /// Creates an object by specifying a . /// @@ -589,9 +601,7 @@ public static IResult StatusCode(int statusCode) /// /// The value to be included in the HTTP response body. /// The created for the response. -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. public static IResult NotFound(object? value = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. => NotFound(value); /// @@ -614,9 +624,7 @@ public static IResult Unauthorized() /// /// An error object to be included in the HTTP response body. /// The created for the response. -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. public static IResult BadRequest(object? error = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. => BadRequest(error); /// @@ -632,9 +640,7 @@ public static IResult BadRequest(TValue? error) /// /// An error object to be included in the HTTP response body. /// The created for the response. -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. public static IResult Conflict(object? error = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. => Conflict(error); /// @@ -657,9 +663,7 @@ public static IResult NoContent() /// /// The value to be included in the HTTP response body. /// The created for the response. -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. public static IResult Ok(object? value = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. => Ok(value); /// @@ -675,9 +679,7 @@ public static IResult Ok(TValue? value) /// /// An error object to be included in the HTTP response body. /// The created for the response. -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. public static IResult UnprocessableEntity(object? error = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. => UnprocessableEntity(error); /// @@ -824,9 +826,31 @@ public static IResult CreatedAtRoute(string? routeName = null, object? routeValu /// The route data to use for generating the URL. /// The value to be included in the HTTP response body. /// The created for the response. - [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public static IResult CreatedAtRoute(string? routeName, RouteValueDictionary? routeValues, object? value = null) +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters + => CreatedAtRoute(routeName, routeValues, value); + + /// + /// Produces a response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to be included in the HTTP response body. + /// The created for the response. + [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] public static IResult CreatedAtRoute(string? routeName = null, object? routeValues = null, TValue? value = default) + => value is null ? TypedResults.CreatedAtRoute(routeName, routeValues) : TypedResults.CreatedAtRoute(value, routeName, routeValues); + + /// + /// Produces a response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to be included in the HTTP response body. + /// The created for the response. +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public static IResult CreatedAtRoute(string? routeName, RouteValueDictionary? routeValues, TValue? value = default) #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters => value is null ? TypedResults.CreatedAtRoute(routeName, routeValues) : TypedResults.CreatedAtRoute(value, routeName, routeValues); @@ -845,9 +869,7 @@ public static IResult Accepted(string? uri = null, object? value = null) /// The URI with the location at which the status of requested content can be monitored. /// The optional content value to format in the response body. /// The created for the response. -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static IResult Accepted(string? uri = null, TValue? value = default) -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters => value is null ? TypedResults.Accepted(uri) : TypedResults.Accepted(uri, value); /// @@ -858,9 +880,7 @@ public static IResult Accepted(string? uri = null, TValue? value = defau /// The optional content value to format in the response body. /// The created for the response. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] -#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. public static IResult AcceptedAtRoute(string? routeName = null, object? routeValues = null, object? value = null) -#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. => AcceptedAtRoute(routeName, routeValues, value); /// @@ -870,9 +890,31 @@ public static IResult AcceptedAtRoute(string? routeName = null, object? routeVal /// The route data to use for generating the URL. /// The optional content value to format in the response body. /// The created for the response. - [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public static IResult AcceptedAtRoute(string? routeName, RouteValueDictionary? routeValues, object? value = null) +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters + => AcceptedAtRoute(routeName, routeValues, value); + + /// + /// Produces a response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The optional content value to format in the response body. + /// The created for the response. + [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] public static IResult AcceptedAtRoute(string? routeName = null, object? routeValues = null, TValue? value = default) + => value is null ? TypedResults.AcceptedAtRoute(routeName, routeValues) : TypedResults.AcceptedAtRoute(value, routeName, routeValues); + + /// + /// Produces a response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The optional content value to format in the response body. + /// The created for the response. +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public static IResult AcceptedAtRoute(string? routeName, RouteValueDictionary? routeValues, TValue? value = default) #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters => value is null ? TypedResults.AcceptedAtRoute(routeName, routeValues) : TypedResults.AcceptedAtRoute(value, routeName, routeValues); diff --git a/src/Http/Http.Results/src/TypedResults.cs b/src/Http/Http.Results/src/TypedResults.cs index ceb2cea29bb2..4f6bc38d36e0 100644 --- a/src/Http/Http.Results/src/TypedResults.cs +++ b/src/Http/Http.Results/src/TypedResults.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http; @@ -630,6 +631,31 @@ public static RedirectToRouteHttpResult RedirectToRoute(string? routeName = null preserveMethod: preserveMethod, fragment: fragment); + /// + /// Redirects to the specified route. + /// + /// When and are set, sets the status code. + /// When is set, sets the status code. + /// When is set, sets the status code. + /// Otherwise, configures . + /// + /// + /// The name of the route. + /// The parameters for a route. + /// 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. + /// The fragment to add to the URL. + /// The created for the response. +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + public static RedirectToRouteHttpResult RedirectToRoute(string? routeName, RouteValueDictionary? routeValues, bool permanent = false, bool preserveMethod = false, string? fragment = null) +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters + => new( + routeName: routeName, + routeValues: routeValues, + permanent: permanent, + preserveMethod: preserveMethod, + fragment: fragment); + /// /// Creates a object by specifying a . /// @@ -869,7 +895,18 @@ public static Created Created(Uri? uri, TValue? value) /// The route data to use for generating the URL. /// The created for the response. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] +#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads public static CreatedAtRoute CreatedAtRoute(string? routeName = null, object? routeValues = null) +#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads + => new(routeName, routeValues); + + /// + /// Produces a response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The created for the response. + public static CreatedAtRoute CreatedAtRoute(string? routeName, RouteValueDictionary? routeValues) => new(routeName, routeValues); /// @@ -881,7 +918,20 @@ public static CreatedAtRoute CreatedAtRoute(string? routeName = null, object? ro /// The value to be included in the HTTP response body. /// The created for the response. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] +#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads public static CreatedAtRoute CreatedAtRoute(TValue? value, string? routeName = null, object? routeValues = null) +#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads + => new(routeName, routeValues, value); + + /// + /// Produces a response. + /// + /// The type of object that will be JSON serialized to the response body. + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to be included in the HTTP response body. + /// The created for the response. + public static CreatedAtRoute CreatedAtRoute(TValue? value, string? routeName, RouteValueDictionary? routeValues) => new(routeName, routeValues, value); /// @@ -935,7 +985,18 @@ public static Accepted Accepted(Uri uri, TValue? value) /// The route data to use for generating the URL. /// The created for the response. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] +#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads public static AcceptedAtRoute AcceptedAtRoute(string? routeName = null, object? routeValues = null) +#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads + => new(routeName, routeValues); + + /// + /// Produces a response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The created for the response. + public static AcceptedAtRoute AcceptedAtRoute(string? routeName, RouteValueDictionary? routeValues) => new(routeName, routeValues); /// @@ -947,7 +1008,20 @@ public static AcceptedAtRoute AcceptedAtRoute(string? routeName = null, object? /// The value to be included in the HTTP response body. /// The created for the response. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] +#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads public static AcceptedAtRoute AcceptedAtRoute(TValue? value, string? routeName = null, object? routeValues = null) +#pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads + => new(routeName, routeValues, value); + + /// + /// Produces a response. + /// + /// The type of object that will be JSON serialized to the response body. + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to be included in the HTTP response body. + /// The created for the response. + public static AcceptedAtRoute AcceptedAtRoute(TValue? value, string? routeName, RouteValueDictionary? routeValues) => new(routeName, routeValues, value); /// diff --git a/src/Http/Http.Results/test/ResultsTests.cs b/src/Http/Http.Results/test/ResultsTests.cs index c9abea1a6803..c2cbe90b2d00 100644 --- a/src/Http/Http.Results/test/ResultsTests.cs +++ b/src/Http/Http.Results/test/ResultsTests.cs @@ -94,6 +94,38 @@ public void AcceptedAtRoute_WithRouteNameAndRouteValuesAndValue_ResultHasCorrect Assert.Equal(value, result.Value); } + [Fact] + public void AcceptedAtRoute_WithRouteNameAndRouteValueDictionaryAndValue_ResultHasCorrectValues() + { + // Arrange + var routeName = "routeName"; + var routeValues = new RouteValueDictionary { ["foo"] = 123 }; + object value = new { }; + + // Act + var result = Results.AcceptedAtRoute(routeName, routeValues, value) as AcceptedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Equal(routeName, result.RouteName); + Assert.Equal(routeValues, result.RouteValues); + Assert.Equal(value, result.Value); + } + + [Fact] + public void AcceptedAtRoute_WithNullRouteNameAndRouteValues_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = Results.AcceptedAtRoute(null, null) as AcceptedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + [Fact] public void AcceptedAtRouteOfT_WithRouteNameAndRouteValuesAndValue_ResultHasCorrectValues() { @@ -112,6 +144,52 @@ public void AcceptedAtRouteOfT_WithRouteNameAndRouteValuesAndValue_ResultHasCorr Assert.Equal(value, result.Value); } + [Fact] + public void AcceptedAtRouteOfT_WithRouteNameAndRouteValueDictionaryAndValue_ResultHasCorrectValues() + { + // Arrange + var routeName = "routeName"; + var routeValues = new RouteValueDictionary { ["foo"] = 123 }; + var value = new Todo(1); + + // Act + var result = Results.AcceptedAtRoute(routeName, routeValues, value) as AcceptedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Equal(routeName, result.RouteName); + Assert.Equal(routeValues, result.RouteValues); + Assert.Equal(value, result.Value); + } + + [Fact] + public void AcceptedAtRouteOfT_WithNullRouteNameAndRouteValueDictionaryAndValue_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = Results.AcceptedAtRoute(null, null, null) as AcceptedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + + [Fact] + public void AcceptedAtRouteOfT_WithNullRouteNameAndRouteValuesAndValue_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = Results.AcceptedAtRoute(null, null, null) as AcceptedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + [Fact] public void AcceptedAtRoute_WithRouteNameAndRouteValues_ResultHasCorrectValues() { @@ -642,6 +720,52 @@ public void CreatedAtRoute_WithRouteNameAndRouteValuesAndValue_ResultHasCorrectV Assert.Equal(value, result.Value); } + [Fact] + public void CreatedAtRoute_WithRouteNameAndRouteValueDictionaryAndValue_ResultHasCorrectValues() + { + // Arrange + var routeName = "routeName"; + var routeValues = new RouteValueDictionary { ["foo"] = 123 }; + object value = new { }; + + // Act + var result = Results.CreatedAtRoute(routeName, routeValues, value) as CreatedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(routeName, result.RouteName); + Assert.Equal(routeValues, result.RouteValues); + Assert.Equal(value, result.Value); + } + + [Fact] + public void CreatedAtRoute_WithNullRouteNameAndRouteValues_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = Results.CreatedAtRoute(null, null) as CreatedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + + [Fact] + public void CreatedAtRoute_WithNullRouteNameAndRouteValuesAndValue_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = Results.CreatedAtRoute(null, null, null) as CreatedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + [Fact] public void CreatedAtRouteOfT_WithRouteNameAndRouteValuesAndValue_ResultHasCorrectValues() { @@ -660,6 +784,24 @@ public void CreatedAtRouteOfT_WithRouteNameAndRouteValuesAndValue_ResultHasCorre Assert.Equal(value, result.Value); } + [Fact] + public void CreatedAtRouteOfT_WithRouteNameAndRouteValueDictionaryAndValue_ResultHasCorrectValues() + { + // Arrange + var routeName = "routeName"; + var routeValues = new RouteValueDictionary { ["foo"] = 123 }; + var value = new Todo(1); + + // Act + var result = Results.CreatedAtRoute(routeName, routeValues, value) as CreatedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(routeName, result.RouteName); + Assert.Equal(routeValues, result.RouteValues); + Assert.Equal(value, result.Value); + } + [Fact] public void CreatedAtRoute_WithRouteNameAndValue_ResultHasCorrectValues() { @@ -694,6 +836,20 @@ public void CreatedAtRouteOfT_WithRouteNameAndValue_ResultHasCorrectValues() Assert.Equal(value, result.Value); } + [Fact] + public void CreatedAtRouteOfT_WithNullRouteNameAndRouteValues_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = Results.CreatedAtRoute(null, null, null) as CreatedAtRoute; + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + [Fact] public void CreatedAtRoute_WithRouteName_ResultHasCorrectValues() { @@ -1259,6 +1415,25 @@ public void RedirectToRoute_WithRouteNameAndRouteValuesAndFragment_ResultHasCorr Assert.Equal(fragment, result.Fragment); } + [Fact] + public void RedirectToRoute_WithRouteNameAndRouteValueDictionaryAndFragment_ResultHasCorrectValues() + { + // Arrange + var routeName = "routeName"; + var routeValues = new RouteValueDictionary { ["foo"] = 123 }; + var fragment = "test"; + + // Act + var result = Results.RedirectToRoute(routeName, routeValues, true, true, fragment) as RedirectToRouteHttpResult; + + // Assert + Assert.Equal(routeName, result.RouteName); + Assert.Equal(routeValues, result.RouteValues); + Assert.True(result.Permanent); + Assert.True(result.PreserveMethod); + Assert.Equal(fragment, result.Fragment); + } + [Fact] public void RedirectToRoute_WithNoArgs_ResultHasCorrectValues() { @@ -1267,7 +1442,18 @@ public void RedirectToRoute_WithNoArgs_ResultHasCorrectValues() // Assert Assert.Null(result.RouteName); - Assert.Null(result.RouteValues); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + + [Fact] + public void RedirectToRoute_WithNoArgs_RouteValueDictionary_ResultHasCorrectValues() + { + // Act + var result = Results.RedirectToRoute(null, null) as RedirectToRouteHttpResult; + + // Assert + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); } [Fact] @@ -1425,6 +1611,8 @@ private static string GetMemberName(Expression expression) { (() => Results.Accepted(null, null), typeof(Accepted)), (() => Results.Accepted(null, new()), typeof(Accepted)), + (() => Results.AcceptedAtRoute("routeName", (object)null, null), typeof(AcceptedAtRoute)), + (() => Results.AcceptedAtRoute("routeName", (object)null, new()), typeof(AcceptedAtRoute)), (() => Results.AcceptedAtRoute("routeName", null, null), typeof(AcceptedAtRoute)), (() => Results.AcceptedAtRoute("routeName", null, new()), typeof(AcceptedAtRoute)), (() => Results.BadRequest(null), typeof(BadRequest)), @@ -1438,6 +1626,8 @@ private static string GetMemberName(Expression expression) (() => Results.Created("/path", null), typeof(Created)), (() => Results.Created(), typeof(Created)), (() => Results.Created("/path", new()), typeof(Created)), + (() => Results.CreatedAtRoute("routeName", (object)null, null), typeof(CreatedAtRoute)), + (() => Results.CreatedAtRoute("routeName", (object)null, new()), typeof(CreatedAtRoute)), (() => Results.CreatedAtRoute("routeName", null, null), typeof(CreatedAtRoute)), (() => Results.CreatedAtRoute("routeName", null, new()), typeof(CreatedAtRoute)), (() => Results.Empty, typeof(EmptyHttpResult)), @@ -1459,6 +1649,7 @@ private static string GetMemberName(Expression expression) (() => Results.Text("content", null, null, null), typeof(ContentHttpResult)), (() => Results.Redirect("/path", false, false), typeof(RedirectHttpResult)), (() => Results.LocalRedirect("/path", false, false), typeof(RedirectHttpResult)), + (() => Results.RedirectToRoute("routeName", (object)null, false, false, null), typeof(RedirectToRouteHttpResult)), (() => Results.RedirectToRoute("routeName", null, false, false, null), typeof(RedirectToRouteHttpResult)), (() => Results.SignIn(new(), null, null), typeof(SignInHttpResult)), (() => Results.SignOut(new(), null), typeof(SignOutHttpResult)), diff --git a/src/Http/Http.Results/test/TypedResultsTests.cs b/src/Http/Http.Results/test/TypedResultsTests.cs index 93044558ef5b..5345fd71f14f 100644 --- a/src/Http/Http.Results/test/TypedResultsTests.cs +++ b/src/Http/Http.Results/test/TypedResultsTests.cs @@ -137,6 +137,34 @@ public void AcceptedAtRoute_WithRouteNameAndRouteValues_ResultHasCorrectValues() Assert.Equal(new RouteValueDictionary(routeValues), result.RouteValues); } + [Fact] + public void AcceptedAtRoute_WithNullRouteNameAndRouteValues_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = TypedResults.AcceptedAtRoute(null, null); + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + + [Fact] + public void AcceptedAtRouteOfT_WithNullRouteNameAndRouteValuesAndValue_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = TypedResults.AcceptedAtRoute(null, null, null); + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + [Fact] public void AcceptedAtRoute_WithNoArgs_ResultHasCorrectValues() { @@ -149,6 +177,38 @@ public void AcceptedAtRoute_WithNoArgs_ResultHasCorrectValues() Assert.NotNull(result.RouteValues); } + [Fact] + public void AcceptedAtRoute_WithRouteValues_ResultHasCorrectValues() + { + // Arrange + var routeValues = new { foo = 123 }; + + // Act + var result = TypedResults.AcceptedAtRoute(routeValues: routeValues); + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(routeValues), result.RouteValues); + } + + [Fact] + public void AcceptedAtRoute_WithRouteValuesAndValue_ResultHasCorrectValues() + { + // Arrange + var routeValues = new { foo = 123 }; + var value = new { }; + + // Act + var result = TypedResults.AcceptedAtRoute(value: value, routeValues: routeValues); + + // Assert + Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(routeValues), result.RouteValues); + Assert.Equal(value, result.Value); + } + [Fact] public void BadRequest_WithValue_ResultHasCorrectValues() { @@ -689,6 +749,66 @@ public void CreatedAtRoute_WithNoArgs_ResultHasCorrectValues() Assert.Equal(new RouteValueDictionary(), result.RouteValues); } + [Fact] + public void CreatedAtRoute_WithRouteValues_ResultHasCorrectValues() + { + // Arrange + var routeValues = new { foo = 123 }; + + // Act + var result = TypedResults.CreatedAtRoute(routeValues: routeValues); + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(routeValues), result.RouteValues); + } + + [Fact] + public void CreatedAtRoute_WithRouteValuesAndValue_ResultHasCorrectValues() + { + // Arrange + var routeValues = new { foo = 123 }; + var value = new { }; + + // Act + var result = TypedResults.CreatedAtRoute(value: value, routeValues: routeValues); + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(routeValues), result.RouteValues); + Assert.Equal(value, result.Value); + } + + [Fact] + public void CreatedAtRoute_WithNullRouteNameAndRouteValues_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = TypedResults.CreatedAtRoute(null, null); + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + + [Fact] + public void CreatedAtRouteOfT_WithNullRouteNameAndRouteValuesAndValue_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = TypedResults.CreatedAtRoute(null, null, null); + + // Assert + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + [Fact] public void Empty_IsEmptyInstance() { @@ -1151,7 +1271,34 @@ public void RedirectToRoute_WithNoArgs_ResultHasCorrectValues() // Assert Assert.Null(result.RouteName); - Assert.Null(result.RouteValues); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); + } + + [Fact] + public void RedirectToRoute_WithRouteValues_ResultHasCorrectValues() + { + // Arrange + var routeValues = new { foo = 123 }; + + // Act + var result = TypedResults.RedirectToRoute(routeValues: routeValues); + + // Assert + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(routeValues), result.RouteValues); + } + + [Fact] + public void RedirectToRoute_WithNullRouteNameAndRouteValues_ResultHasCorrectValues() + { + // Arrange + + // Act + var result = TypedResults.RedirectToRoute(null, null); + + // Assert + Assert.Null(result.RouteName); + Assert.Equal(new RouteValueDictionary(), result.RouteValues); } [Fact]