Skip to content

Move EmptyHttpResult to Http.Abstractions #46077

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Microsoft.AspNetCore.Http.EndpointFilterInvocationContext
Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.EndpointFilterInvocationContext() -> void
Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object!
Microsoft.AspNetCore.Http.EndpointMetadataCollection.GetRequiredMetadata<T>() -> T!
Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult
Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task!
Microsoft.AspNetCore.Http.HttpValidationProblemDetails
Microsoft.AspNetCore.Http.HttpValidationProblemDetails.Errors.get -> System.Collections.Generic.IDictionary<string!, string![]!>!
Microsoft.AspNetCore.Http.HttpValidationProblemDetails.HttpValidationProblemDetails() -> void
Expand Down Expand Up @@ -91,3 +93,4 @@ Microsoft.AspNetCore.Mvc.ProblemDetails.Type.set -> void
override Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.Arguments.get -> System.Collections.Generic.IList<object?>!
override Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.GetArgument<T>(int index) -> T
override Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
static Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.Instance.get -> Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult!
19 changes: 6 additions & 13 deletions src/Http/Http.Extensions/src/RequestDelegateFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Text.Json.Serialization.Metadata;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Internal;
Expand Down Expand Up @@ -96,11 +97,7 @@ public static partial class RequestDelegateFactory
private static readonly MemberExpression FormFilesExpr = Expression.Property(FormExpr, typeof(IFormCollection).GetProperty(nameof(IFormCollection.Files))!);
private static readonly MemberExpression StatusCodeExpr = Expression.Property(HttpResponseExpr, typeof(HttpResponse).GetProperty(nameof(HttpResponse.StatusCode))!);
private static readonly MemberExpression CompletedTaskExpr = Expression.Property(null, (PropertyInfo)GetMemberInfo<Func<Task>>(() => Task.CompletedTask));
// Due to https://github.com/dotnet/aspnetcore/issues/41330 we cannot reference the EmptyHttpResult type
// but users still need to assert on it as in https://github.com/dotnet/aspnetcore/issues/45063
// so we temporarily work around this here by using reflection to get the actual type.
private static readonly object? EmptyHttpResultInstance = Type.GetType("Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult, Microsoft.AspNetCore.Http.Results")?.GetProperty("Instance")?.GetValue(null, null);
private static readonly NewExpression EmptyHttpResultValueTaskExpr = Expression.New(typeof(ValueTask<object>).GetConstructor(new[] { typeof(IResult) })!, Expression.Constant(EmptyHttpResultInstance));
private static readonly NewExpression EmptyHttpResultValueTaskExpr = Expression.New(typeof(ValueTask<object>).GetConstructor(new[] { typeof(EmptyHttpResult) })!, Expression.Property(null, typeof(EmptyHttpResult), nameof(EmptyHttpResult.Instance)));
private static readonly ParameterExpression TempSourceStringExpr = ParameterBindingMethodCache.TempSourceStringExpr;
private static readonly BinaryExpression TempSourceStringNotNullExpr = Expression.NotEqual(TempSourceStringExpr, Expression.Constant(null));
private static readonly BinaryExpression TempSourceStringNullExpr = Expression.Equal(TempSourceStringExpr, Expression.Constant(null));
Expand Down Expand Up @@ -397,7 +394,6 @@ private static Expression[] CreateArgumentsAndInferMetadata(MethodInfo methodInf
private static EndpointFilterDelegate? CreateFilterPipeline(MethodInfo methodInfo, Expression? targetExpression, RequestDelegateFactoryContext factoryContext, Expression<Func<HttpContext, object?>>? targetFactory)
{
Debug.Assert(factoryContext.EndpointBuilder.FilterFactories.Count > 0);
Debug.Assert(EmptyHttpResultInstance is not null, "The EmptyHttpResult type could not be found.");
// httpContext.Response.StatusCode >= 400
// ? Task.CompletedTask
// : {
Expand Down Expand Up @@ -487,7 +483,6 @@ targetExpression is null

private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall, Type returnType)
{
Debug.Assert(EmptyHttpResultInstance is not null, "The EmptyHttpResult type could not be found.");
if (returnType == typeof(void))
{
return Expression.Block(methodCall, EmptyHttpResultValueTaskExpr);
Expand Down Expand Up @@ -2209,34 +2204,32 @@ static async Task ExecuteAwaited(ValueTask task)

private static ValueTask<object?> ExecuteTaskWithEmptyResult(Task task)
{
Debug.Assert(EmptyHttpResultInstance is not null, "The EmptyHttpResult type could not be found.");
static async ValueTask<object?> ExecuteAwaited(Task task)
{
await task;
return EmptyHttpResultInstance;
return EmptyHttpResult.Instance;
}

if (task.IsCompletedSuccessfully)
{
return new ValueTask<object?>(EmptyHttpResultInstance);
return new ValueTask<object?>(EmptyHttpResult.Instance);
}

return ExecuteAwaited(task);
}

private static ValueTask<object?> ExecuteValueTaskWithEmptyResult(ValueTask valueTask)
{
Debug.Assert(EmptyHttpResultInstance is not null, "The EmptyHttpResult type could not be found.");
static async ValueTask<object?> ExecuteAwaited(ValueTask task)
{
await task;
return EmptyHttpResultInstance;
return EmptyHttpResult.Instance;
}

if (valueTask.IsCompletedSuccessfully)
{
valueTask.GetAwaiter().GetResult();
return new ValueTask<object?>(EmptyHttpResultInstance);
return new ValueTask<object?>(EmptyHttpResult.Instance);
}

return ExecuteAwaited(valueTask);
Expand Down
6 changes: 6 additions & 0 deletions src/Http/Http.Results/src/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +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 System.Runtime.CompilerServices;

[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult))]
9 changes: 6 additions & 3 deletions src/Http/Http.Results/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
#nullable enable
*REMOVED*Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult
*REMOVED*Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task!
*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)
static Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.Instance.get -> Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult! (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Microsoft.AspNetCore.Http.HttpResults.Accepted
Microsoft.AspNetCore.Http.HttpResults.Accepted.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task!
Microsoft.AspNetCore.Http.HttpResults.Accepted.Location.get -> string?
Expand Down Expand Up @@ -62,8 +68,6 @@ Microsoft.AspNetCore.Http.HttpResults.CreatedAtRoute<TValue>.RouteName.get -> st
Microsoft.AspNetCore.Http.HttpResults.CreatedAtRoute<TValue>.RouteValues.get -> Microsoft.AspNetCore.Routing.RouteValueDictionary!
Microsoft.AspNetCore.Http.HttpResults.CreatedAtRoute<TValue>.StatusCode.get -> int
Microsoft.AspNetCore.Http.HttpResults.CreatedAtRoute<TValue>.Value.get -> TValue?
Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult
Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task!
Microsoft.AspNetCore.Http.HttpResults.FileContentHttpResult
Microsoft.AspNetCore.Http.HttpResults.FileContentHttpResult.ContentType.get -> string!
Microsoft.AspNetCore.Http.HttpResults.FileContentHttpResult.EnableRangeProcessing.get -> bool
Expand Down Expand Up @@ -234,7 +238,6 @@ Microsoft.AspNetCore.Http.HttpResults.VirtualFileHttpResult.FileLength.get -> lo
Microsoft.AspNetCore.Http.HttpResults.VirtualFileHttpResult.FileName.get -> string!
Microsoft.AspNetCore.Http.HttpResults.VirtualFileHttpResult.LastModified.get -> System.DateTimeOffset?
Microsoft.AspNetCore.Http.TypedResults
static Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult.Instance.get -> Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult!
static Microsoft.AspNetCore.Http.Results.Bytes(System.ReadOnlyMemory<byte> 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<System.IO.Stream!, System.Threading.Tasks.Task!>! streamWriterCallback, string? contentType = null, string? fileDownloadName = null, System.DateTimeOffset? lastModified = null, Microsoft.Net.Http.Headers.EntityTagHeaderValue? entityTag = null) -> Microsoft.AspNetCore.Http.IResult!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Text.Json.Serialization.Metadata;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -13,11 +14,6 @@ namespace Microsoft.AspNetCore.Routing;

internal static class RequestDelegateFilterPipelineBuilder
{
// Due to https://github.com/dotnet/aspnetcore/issues/41330 we cannot reference the EmptyHttpResult type
// but users still need to assert on it as in https://github.com/dotnet/aspnetcore/issues/45063
// so we temporarily work around this here by using reflection to get the actual type.
private static readonly object? EmptyHttpResultInstance = Type.GetType("Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult, Microsoft.AspNetCore.Http.Results")?.GetProperty("Instance")?.GetValue(null, null);

public static RequestDelegate Create(RequestDelegate requestDelegate, RequestDelegateFactoryOptions options)
{
Debug.Assert(options.EndpointBuilder != null);
Expand All @@ -35,12 +31,11 @@ public static RequestDelegate Create(RequestDelegate requestDelegate, RequestDel

EndpointFilterDelegate filteredInvocation = async (EndpointFilterInvocationContext context) =>
{
Debug.Assert(EmptyHttpResultInstance != null, "Unable to get EmptyHttpResult instance via reflection.");
if (context.HttpContext.Response.StatusCode < 400)
{
await requestDelegate(context.HttpContext);
}
return EmptyHttpResultInstance;
return EmptyHttpResult.Instance;
};

var initialFilteredInvocation = filteredInvocation;
Expand Down