Skip to content

Commit 837b178

Browse files
authored
Cleanup Results helper (#34296)
* Cleanup Results helper * Add Results.Bytes, Results.Stream, and Results.Text overloads. * Consolidate Physical and Virtual files helpers. * Use default arguments to consolidate overloads. * Remove Accepted(Uri?) / Accepted(Uri?, object? value) overload to remove ambiguity with AcceptedAt(string?, object? value);
1 parent 00aa186 commit 837b178

29 files changed

+375
-1705
lines changed

src/Http/Http.Extensions/src/ResponseHeaders.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public DateTimeOffset? Expires
153153
}
154154

155155
/// <summary>
156-
/// Gets or sets the <c>Last=Modified</c> header for an HTTP response.
156+
/// Gets or sets the <c>Last-Modified</c> header for an HTTP response.
157157
/// </summary>
158158
public DateTimeOffset? LastModified
159159
{

src/Http/Http.Results/src/AcceptedAtRouteResult.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System;
54
using Microsoft.AspNetCore.Routing;
65
using Microsoft.Extensions.DependencyInjection;
76

@@ -48,7 +47,7 @@ public AcceptedAtRouteResult(
4847
public RouteValueDictionary RouteValues { get; }
4948

5049
/// <inheritdoc />
51-
protected override void OnFormatting(HttpContext context)
50+
protected override void ConfigureResponseHeaders(HttpContext context)
5251
{
5352
var linkGenerator = context.RequestServices.GetRequiredService<LinkGenerator>();
5453
var url = linkGenerator.GetUriByAddress(

src/Http/Http.Results/src/AcceptedResult.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,8 @@ public AcceptedResult(Uri locationUri, object? value)
5858
public string? Location { get; set; }
5959

6060
/// <inheritdoc />
61-
protected override void OnFormatting(HttpContext context)
61+
protected override void ConfigureResponseHeaders(HttpContext context)
6262
{
63-
if (context == null)
64-
{
65-
throw new ArgumentNullException(nameof(context));
66-
}
67-
6863
if (!string.IsNullOrEmpty(Location))
6964
{
7065
context.Response.Headers.Location = Location;

src/Http/Http.Results/src/BadRequestResult.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Http/Http.Results/src/ConflictResult.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Http/Http.Results/src/CreatedAtRouteResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public CreatedAtRouteResult(
4848
public RouteValueDictionary? RouteValues { get; set; }
4949

5050
/// <inheritdoc />
51-
protected override void OnFormatting(HttpContext context)
51+
protected override void ConfigureResponseHeaders(HttpContext context)
5252
{
5353
var linkGenerator = context.RequestServices.GetRequiredService<LinkGenerator>();
5454
var url = linkGenerator.GetUriByRouteValues(

src/Http/Http.Results/src/CreatedResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public CreatedResult(Uri location, object? value)
4949
public string Location { get; init; }
5050

5151
/// <inheritdoc />
52-
protected override void OnFormatting(HttpContext context)
52+
protected override void ConfigureResponseHeaders(HttpContext context)
5353
{
5454
context.Response.Headers.Location = Location;
5555
}

src/Http/Http.Results/src/FileContentResult.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System.IO;
5-
using System.Threading.Tasks;
64
using Microsoft.AspNetCore.Internal;
75
using Microsoft.Extensions.DependencyInjection;
86
using Microsoft.Extensions.Logging;
@@ -18,7 +16,7 @@ internal sealed partial class FileContentResult : FileResult, IResult
1816
/// </summary>
1917
/// <param name="fileContents">The bytes that represent the file contents.</param>
2018
/// <param name="contentType">The Content-Type header of the response.</param>
21-
public FileContentResult(byte[] fileContents, string contentType)
19+
public FileContentResult(byte[] fileContents, string? contentType)
2220
: base(contentType)
2321
{
2422
FileContents = fileContents;

src/Http/Http.Results/src/FileResult.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System;
54
using System.Diagnostics.CodeAnalysis;
65
using Microsoft.Extensions.Logging;
76
using Microsoft.Net.Http.Headers;
@@ -17,14 +16,9 @@ internal abstract partial class FileResult
1716
/// the provided <paramref name="contentType"/>.
1817
/// </summary>
1918
/// <param name="contentType">The Content-Type header of the response.</param>
20-
protected FileResult(string contentType)
19+
protected FileResult(string? contentType)
2120
{
22-
if (contentType == null)
23-
{
24-
throw new ArgumentNullException(nameof(contentType));
25-
}
26-
27-
ContentType = contentType;
21+
ContentType = contentType ?? "application/octet-stream";
2822
}
2923

3024
/// <summary>

src/Http/Http.Results/src/FileStreamResult.cs

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System;
5-
using System.IO;
6-
using System.Threading.Tasks;
74
using Microsoft.AspNetCore.Internal;
85
using Microsoft.Extensions.DependencyInjection;
96
using Microsoft.Extensions.Logging;
10-
using Microsoft.Net.Http.Headers;
117

128
namespace Microsoft.AspNetCore.Http.Result
139
{
@@ -24,26 +20,8 @@ internal sealed class FileStreamResult : FileResult, IResult
2420
/// </summary>
2521
/// <param name="fileStream">The stream with the file.</param>
2622
/// <param name="contentType">The Content-Type header of the response.</param>
27-
public FileStreamResult(Stream fileStream, string contentType)
28-
: this(fileStream, MediaTypeHeaderValue.Parse(contentType))
29-
{
30-
if (fileStream == null)
31-
{
32-
throw new ArgumentNullException(nameof(fileStream));
33-
}
34-
35-
FileStream = fileStream;
36-
}
37-
38-
/// <summary>
39-
/// Creates a new <see cref="FileStreamResult"/> instance with
40-
/// the provided <paramref name="fileStream"/> and the
41-
/// provided <paramref name="contentType"/>.
42-
/// </summary>
43-
/// <param name="fileStream">The stream with the file.</param>
44-
/// <param name="contentType">The Content-Type header of the response.</param>
45-
public FileStreamResult(Stream fileStream, MediaTypeHeaderValue contentType)
46-
: base(contentType.ToString())
23+
public FileStreamResult(Stream fileStream, string? contentType)
24+
: base(contentType)
4725
{
4826
if (fileStream == null)
4927
{
@@ -58,10 +36,10 @@ public FileStreamResult(Stream fileStream, MediaTypeHeaderValue contentType)
5836
/// </summary>
5937
public Stream FileStream { get; }
6038

61-
public Task ExecuteAsync(HttpContext httpContext)
39+
public async Task ExecuteAsync(HttpContext httpContext)
6240
{
6341
var logger = httpContext.RequestServices.GetRequiredService<ILogger<FileStreamResult>>();
64-
using (FileStream)
42+
await using (FileStream)
6543
{
6644
Log.ExecutingFileResult(logger, this);
6745

@@ -90,20 +68,20 @@ public Task ExecuteAsync(HttpContext httpContext)
9068

9169
if (!serveBody)
9270
{
93-
return Task.CompletedTask;
71+
return;
9472
}
9573

9674
if (range != null && rangeLength == 0)
9775
{
98-
return Task.CompletedTask;
76+
return;
9977
}
10078

10179
if (range != null)
10280
{
10381
FileResultHelper.Log.WritingRangeToBody(logger);
10482
}
10583

106-
return FileResultHelper.WriteFileAsync(httpContext, FileStream, range, rangeLength);
84+
await FileResultHelper.WriteFileAsync(httpContext, FileStream, range, rangeLength);
10785
}
10886
}
10987
}

src/Http/Http.Results/src/NotFoundResult.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Http/Http.Results/src/ObjectResult.cs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System.Threading.Tasks;
54
using Microsoft.AspNetCore.Http.Extensions;
65
using Microsoft.AspNetCore.Mvc;
76
using Microsoft.Extensions.DependencyInjection;
@@ -42,7 +41,7 @@ public Task ExecuteAsync(HttpContext httpContext)
4241
{
4342
var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>();
4443
var logger = loggerFactory.CreateLogger(GetType());
45-
Log.ObjectResultExecuting(logger, Value);
44+
Log.ObjectResultExecuting(logger, Value, StatusCode);
4645

4746
if (Value is ProblemDetails problemDetails)
4847
{
@@ -54,10 +53,25 @@ public Task ExecuteAsync(HttpContext httpContext)
5453
httpContext.Response.StatusCode = statusCode;
5554
}
5655

56+
ConfigureResponseHeaders(httpContext);
57+
58+
if (Value is null)
59+
{
60+
return Task.CompletedTask;
61+
}
62+
5763
OnFormatting(httpContext);
5864
return httpContext.Response.WriteAsJsonAsync(Value);
5965
}
6066

67+
protected virtual void OnFormatting(HttpContext httpContext)
68+
{
69+
}
70+
71+
protected virtual void ConfigureResponseHeaders(HttpContext httpContext)
72+
{
73+
}
74+
6175
private void ApplyProblemDetailsDefaults(ProblemDetails problemDetails)
6276
{
6377
// We allow StatusCode to be specified either on ProblemDetails or on the ObjectResult and use it to configure the other.
@@ -89,23 +103,29 @@ private void ApplyProblemDetailsDefaults(ProblemDetails problemDetails)
89103
}
90104
}
91105

92-
protected virtual void OnFormatting(HttpContext httpContext)
93-
{
94-
}
95-
96106
private static partial class Log
97107
{
98-
public static void ObjectResultExecuting(ILogger logger, object? value)
108+
public static void ObjectResultExecuting(ILogger logger, object? value, int? statusCode)
99109
{
100110
if (logger.IsEnabled(LogLevel.Information))
101111
{
102-
var valueType = value is null ? "null" : value.GetType().FullName!;
103-
ObjectResultExecuting(logger, valueType);
112+
if (value is null)
113+
{
114+
ObjectResultExecutingWithoutValue(logger, statusCode ?? StatusCodes.Status200OK);
115+
}
116+
else
117+
{
118+
var valueType = value.GetType().FullName!;
119+
ObjectResultExecuting(logger, valueType, statusCode ?? StatusCodes.Status200OK);
120+
}
104121
}
105122
}
106123

107-
[LoggerMessage(1, LogLevel.Information, "Writing value of type '{Type}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)]
108-
public static partial void ObjectResultExecuting(ILogger logger, string type);
124+
[LoggerMessage(1, LogLevel.Information, "Writing value of type '{Type}' with status code '{StatusCode}'.", EventName = "ObjectResultExecuting", SkipEnabledCheck = true)]
125+
private static partial void ObjectResultExecuting(ILogger logger, string type, int statusCode);
126+
127+
[LoggerMessage(2, LogLevel.Information, "Executing result with status code '{StatusCode}'.", EventName = "ObjectResultExecutingWithoutValue", SkipEnabledCheck = true)]
128+
private static partial void ObjectResultExecutingWithoutValue(ILogger logger, int statusCode);
109129
}
110130
}
111131
}

src/Http/Http.Results/src/OkResult.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Http/Http.Results/src/PhysicalFileResult.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System;
5-
using System.IO;
6-
using System.Threading.Tasks;
74
using Microsoft.AspNetCore.Internal;
85
using Microsoft.Extensions.DependencyInjection;
96
using Microsoft.Extensions.Logging;
@@ -22,7 +19,7 @@ internal sealed partial class PhysicalFileResult : FileResult, IResult
2219
/// </summary>
2320
/// <param name="fileName">The path to the file. The path must be an absolute path.</param>
2421
/// <param name="contentType">The Content-Type header of the response.</param>
25-
public PhysicalFileResult(string fileName, string contentType)
22+
public PhysicalFileResult(string fileName, string? contentType)
2623
: base(contentType)
2724
{
2825
FileName = fileName;

0 commit comments

Comments
 (0)