-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Adding IResult
implementation for FileStreamResult and LocalRedirectResult
#32956
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
Changes from all commits
bdc812c
1dcfb6f
e128fa3
1bf722e
e2c05d2
991067f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,8 +5,10 @@ | |||||||||
using System.Diagnostics.CodeAnalysis; | ||||||||||
using System.IO; | ||||||||||
using System.Threading.Tasks; | ||||||||||
using Microsoft.AspNetCore.Http; | ||||||||||
using Microsoft.AspNetCore.Mvc.Infrastructure; | ||||||||||
using Microsoft.Extensions.DependencyInjection; | ||||||||||
using Microsoft.Extensions.Logging; | ||||||||||
using Microsoft.Net.Http.Headers; | ||||||||||
|
||||||||||
namespace Microsoft.AspNetCore.Mvc | ||||||||||
|
@@ -15,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc | |||||||||
/// Represents an <see cref="ActionResult"/> that when executed will | ||||||||||
/// write a file from a stream to the response. | ||||||||||
/// </summary> | ||||||||||
public class FileStreamResult : FileResult | ||||||||||
public class FileStreamResult : FileResult, IResult | ||||||||||
{ | ||||||||||
private Stream _fileStream; | ||||||||||
|
||||||||||
|
@@ -79,5 +81,37 @@ public override Task ExecuteResultAsync(ActionContext context) | |||||||||
var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<FileStreamResult>>(); | ||||||||||
return executor.ExecuteAsync(context, this); | ||||||||||
} | ||||||||||
|
||||||||||
async Task IResult.ExecuteAsync(HttpContext httpContext) | ||||||||||
{ | ||||||||||
var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>(); | ||||||||||
var logger = loggerFactory.CreateLogger<FileStreamResult>(); | ||||||||||
|
||||||||||
Task writeFileAsync(HttpContext httpContext, FileStreamResult result, RangeItemHeaderValue? range, long rangeLength) | ||||||||||
=> FileStreamResultExecutor.WriteFileAsyncInternal(httpContext, this, range, rangeLength, logger!); | ||||||||||
|
||||||||||
(RangeItemHeaderValue? range, long rangeLength, bool serveBody) setHeadersAndLog( | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can these be
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
HttpContext httpContext, | ||||||||||
FileResult result, | ||||||||||
long? fileLength, | ||||||||||
bool enableRangeProcessing, | ||||||||||
DateTimeOffset? lastModified, | ||||||||||
EntityTagHeaderValue? etag) | ||||||||||
=> FileResultExecutorBase.SetHeadersAndLog( | ||||||||||
httpContext, | ||||||||||
this, | ||||||||||
fileLength, | ||||||||||
EnableRangeProcessing, | ||||||||||
LastModified, | ||||||||||
EntityTag, | ||||||||||
logger!); | ||||||||||
|
||||||||||
await FileStreamResultExecutor.ExecuteAsyncInternal( | ||||||||||
httpContext, | ||||||||||
this, | ||||||||||
setHeadersAndLog, | ||||||||||
writeFileAsync, | ||||||||||
Comment on lines
+112
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
logger); | ||||||||||
} | ||||||||||
} | ||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,6 +5,7 @@ | |||||
|
||||||
using System; | ||||||
using System.Threading.Tasks; | ||||||
using Microsoft.AspNetCore.Http; | ||||||
using Microsoft.Extensions.Logging; | ||||||
using Microsoft.Net.Http.Headers; | ||||||
|
||||||
|
@@ -26,6 +27,37 @@ public FileStreamResultExecutor(ILoggerFactory loggerFactory) | |||||
|
||||||
/// <inheritdoc /> | ||||||
public virtual async Task ExecuteAsync(ActionContext context, FileStreamResult result) | ||||||
{ | ||||||
await ExecuteAsyncInternal( | ||||||
context, | ||||||
result, | ||||||
SetHeadersAndLog, | ||||||
WriteFileAsync, | ||||||
Logger); | ||||||
} | ||||||
|
||||||
/// <summary> | ||||||
/// Write the contents of the FileStreamResult to the response body. | ||||||
/// </summary> | ||||||
/// <param name="context">The <see cref="ActionContext"/>.</param> | ||||||
/// <param name="result">The FileStreamResult to write.</param> | ||||||
/// <param name="range">The <see cref="RangeItemHeaderValue"/>.</param> | ||||||
/// <param name="rangeLength">The range length.</param> | ||||||
protected virtual Task WriteFileAsync( | ||||||
ActionContext context, | ||||||
FileStreamResult result, | ||||||
RangeItemHeaderValue? range, | ||||||
long rangeLength) | ||||||
{ | ||||||
return WriteFileAsyncInternal(context.HttpContext, result, range, rangeLength, Logger); | ||||||
} | ||||||
|
||||||
internal static async Task ExecuteAsyncInternal<TContext>( | ||||||
TContext context, | ||||||
FileStreamResult result, | ||||||
Func<TContext, FileStreamResult, long?, bool, DateTimeOffset?, EntityTagHeaderValue?, (RangeItemHeaderValue?, long, bool)> SetHeadersAndLog, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Func<TContext, FileStreamResult, RangeItemHeaderValue?, long, Task> WriteFileAsync, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
ILogger logger) | ||||||
{ | ||||||
if (context == null) | ||||||
{ | ||||||
|
@@ -39,7 +71,7 @@ public virtual async Task ExecuteAsync(ActionContext context, FileStreamResult r | |||||
|
||||||
using (result.FileStream) | ||||||
{ | ||||||
Logger.ExecutingFileResult(result); | ||||||
logger.ExecutingFileResult(result); | ||||||
|
||||||
long? fileLength = null; | ||||||
if (result.FileStream.CanSeek) | ||||||
|
@@ -64,22 +96,11 @@ public virtual async Task ExecuteAsync(ActionContext context, FileStreamResult r | |||||
} | ||||||
} | ||||||
|
||||||
/// <summary> | ||||||
/// Write the contents of the FileStreamResult to the response body. | ||||||
/// </summary> | ||||||
/// <param name="context">The <see cref="ActionContext"/>.</param> | ||||||
/// <param name="result">The FileStreamResult to write.</param> | ||||||
/// <param name="range">The <see cref="RangeItemHeaderValue"/>.</param> | ||||||
/// <param name="rangeLength">The range length.</param> | ||||||
protected virtual Task WriteFileAsync( | ||||||
ActionContext context, | ||||||
FileStreamResult result, | ||||||
RangeItemHeaderValue? range, | ||||||
long rangeLength) | ||||||
internal static Task WriteFileAsyncInternal(HttpContext httpContext, FileStreamResult result, RangeItemHeaderValue? range, long rangeLength, ILogger logger) | ||||||
{ | ||||||
if (context == null) | ||||||
if (httpContext == null) | ||||||
{ | ||||||
throw new ArgumentNullException(nameof(context)); | ||||||
throw new ArgumentNullException(nameof(httpContext)); | ||||||
} | ||||||
|
||||||
if (result == null) | ||||||
|
@@ -94,10 +115,10 @@ protected virtual Task WriteFileAsync( | |||||
|
||||||
if (range != null) | ||||||
{ | ||||||
Logger.WritingRangeToBody(); | ||||||
logger.WritingRangeToBody(); | ||||||
} | ||||||
|
||||||
return WriteFileAsync(context.HttpContext, result.FileStream, range, rangeLength); | ||||||
return WriteFileAsync(httpContext, result.FileStream, range, rangeLength); | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -9,7 +9,6 @@ | |||||||||||||||
using Microsoft.AspNetCore.Mvc.Core; | ||||||||||||||||
using Microsoft.AspNetCore.Mvc.Routing; | ||||||||||||||||
using Microsoft.Extensions.Logging; | ||||||||||||||||
using Microsoft.Net.Http.Headers; | ||||||||||||||||
|
||||||||||||||||
namespace Microsoft.AspNetCore.Mvc.Infrastructure | ||||||||||||||||
{ | ||||||||||||||||
|
@@ -45,36 +44,52 @@ public LocalRedirectResultExecutor(ILoggerFactory loggerFactory, IUrlHelperFacto | |||||||||||||||
/// <inheritdoc /> | ||||||||||||||||
public virtual Task ExecuteAsync(ActionContext context, LocalRedirectResult result) | ||||||||||||||||
{ | ||||||||||||||||
if (context == null) | ||||||||||||||||
var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||
|
||||||||||||||||
return ExecuteAsyncInternal( | ||||||||||||||||
context.HttpContext, | ||||||||||||||||
result, | ||||||||||||||||
urlHelper.IsLocalUrl, | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel a little iffy about this. This is now going to allocate a delegate per invocation. Could we have it fall-back in the implementation instead e.g. internal static Task ExecuteAsyncInternal(
HttpContext httpContext,
LocalRedirectResult result,
IUrlHelper? urlHelper)
{
...
var isLocalUrl = urlHelper?.IsLocalUrl(url) ?? UrlHelperBase.CheckIsLocalUrl(url);
} |
||||||||||||||||
urlHelper.Content, | ||||||||||||||||
_logger); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
internal static Task ExecuteAsyncInternal( | ||||||||||||||||
HttpContext httpContext, | ||||||||||||||||
LocalRedirectResult result, | ||||||||||||||||
Func<string, bool> isLocalUrl, | ||||||||||||||||
Func<string, string> getContent, | ||||||||||||||||
ILogger logger | ||||||||||||||||
) | ||||||||||||||||
Comment on lines
+62
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||
{ | ||||||||||||||||
if (httpContext == null) | ||||||||||||||||
{ | ||||||||||||||||
throw new ArgumentNullException(nameof(context)); | ||||||||||||||||
throw new ArgumentNullException(nameof(httpContext)); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
if (result == null) | ||||||||||||||||
{ | ||||||||||||||||
throw new ArgumentNullException(nameof(result)); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context); | ||||||||||||||||
|
||||||||||||||||
// IsLocalUrl is called to handle Urls starting with '~/'. | ||||||||||||||||
if (!urlHelper.IsLocalUrl(result.Url)) | ||||||||||||||||
if (!isLocalUrl(result.Url)) | ||||||||||||||||
{ | ||||||||||||||||
throw new InvalidOperationException(Resources.UrlNotLocal); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
var destinationUrl = urlHelper.Content(result.Url); | ||||||||||||||||
_logger.LocalRedirectResultExecuting(destinationUrl); | ||||||||||||||||
var destinationUrl = getContent(result.Url); | ||||||||||||||||
logger.LocalRedirectResultExecuting(destinationUrl); | ||||||||||||||||
|
||||||||||||||||
if (result.PreserveMethod) | ||||||||||||||||
{ | ||||||||||||||||
context.HttpContext.Response.StatusCode = result.Permanent ? | ||||||||||||||||
httpContext.Response.StatusCode = result.Permanent ? | ||||||||||||||||
StatusCodes.Status308PermanentRedirect : StatusCodes.Status307TemporaryRedirect; | ||||||||||||||||
context.HttpContext.Response.Headers.Location = destinationUrl; | ||||||||||||||||
httpContext.Response.Headers.Location = destinationUrl; | ||||||||||||||||
} | ||||||||||||||||
else | ||||||||||||||||
{ | ||||||||||||||||
context.HttpContext.Response.Redirect(destinationUrl, result.Permanent); | ||||||||||||||||
httpContext.Response.Redirect(destinationUrl, result.Permanent); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
return Task.CompletedTask; | ||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.