diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs
index 3fc8c444e4..9ca57bdc2f 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs
@@ -1551,7 +1551,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The created for the response.
[NonAction]
@@ -1564,7 +1564,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// Set to true to enable range requests processing.
/// The created for the response.
@@ -1579,7 +1579,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The suggested file name.
/// The created for the response.
@@ -1597,7 +1597,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The suggested file name.
/// Set to true to enable range requests processing.
@@ -1620,7 +1620,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The of when the file was last modified.
/// The associated with the file.
@@ -1641,7 +1641,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The of when the file was last modified.
/// The associated with the file.
@@ -1664,7 +1664,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The suggested file name.
/// The of when the file was last modified.
@@ -1687,7 +1687,7 @@ namespace Microsoft.AspNetCore.Mvc
/// This supports range requests ( or
/// if the range is not satisfiable).
///
- /// The physical path of the file to be returned.
+ /// The path to the file. The path must be an absolute path.
/// The Content-Type of the file.
/// The suggested file name.
/// The of when the file was last modified.
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileContentResultExecutor.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileContentResultExecutor.cs
index 0739c522d4..6d194c52f8 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileContentResultExecutor.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileContentResultExecutor.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@@ -29,6 +30,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
throw new ArgumentNullException(nameof(result));
}
+ Logger.ExecutingFileResult(result);
+
var (range, rangeLength, serveBody) = SetHeadersAndLog(
context,
result,
@@ -62,6 +65,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return Task.CompletedTask;
}
+ if (range != null)
+ {
+ Logger.WritingRangeToBody();
+ }
+
var fileContentStream = new MemoryStream(result.FileContents);
return WriteFileAsync(context.HttpContext, fileContentStream, range, rangeLength);
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs
index 6bd8939bc7..19e5b8d6c7 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs
@@ -56,7 +56,6 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
SetContentType(context, result);
SetContentDispositionHeader(context, result);
- Logger.FileResultExecuting(result.FileDownloadName);
var request = context.HttpContext.Request;
var httpRequestHeaders = request.GetTypedHeaders();
@@ -106,6 +105,10 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
}
}
+ else
+ {
+ Logger.NotEnabledForRangeProcessing();
+ }
}
return (range: null, rangeLength: 0, serveBody);
@@ -150,7 +153,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
response.Headers[HeaderNames.AcceptRanges] = AcceptRangeHeaderValue;
}
- internal static bool IfRangeValid(
+ internal bool IfRangeValid(
RequestHeaders httpRequestHeaders,
DateTimeOffset? lastModified = null,
EntityTagHeaderValue etag = null)
@@ -168,11 +171,13 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
if (lastModified.HasValue && lastModified > ifRange.LastModified)
{
+ Logger.IfRangeLastModifiedPreconditionFailed(lastModified, ifRange.LastModified);
return false;
}
}
else if (etag != null && ifRange.EntityTag != null && !ifRange.EntityTag.Compare(etag, useStrongComparison: true))
{
+ Logger.IfRangeETagPreconditionFailed(etag, ifRange.EntityTag);
return false;
}
}
@@ -181,7 +186,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
}
// Internal for testing
- internal static PreconditionState GetPreconditionState(
+ internal PreconditionState GetPreconditionState(
RequestHeaders httpRequestHeaders,
DateTimeOffset? lastModified = null,
EntityTagHeaderValue etag = null)
@@ -200,6 +205,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
etag: etag,
matchFoundState: PreconditionState.ShouldProcess,
matchNotFoundState: PreconditionState.PreconditionFailed);
+
+ if (ifMatchState == PreconditionState.PreconditionFailed)
+ {
+ Logger.IfMatchPreconditionFailed(etag);
+ }
}
// 14.26 If-None-Match
@@ -229,6 +239,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
var unmodified = ifUnmodifiedSince >= lastModified;
ifUnmodifiedSinceState = unmodified ? PreconditionState.ShouldProcess : PreconditionState.PreconditionFailed;
+
+ if (ifUnmodifiedSinceState == PreconditionState.PreconditionFailed)
+ {
+ Logger.IfUnmodifiedSincePreconditionFailed(lastModified, ifUnmodifiedSince);
+ }
}
var state = GetMaxPreconditionState(ifMatchState, ifNoneMatchState, ifModifiedSinceState, ifUnmodifiedSinceState);
@@ -273,7 +288,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return max;
}
- private static (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetRangeHeaders(
+ private (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetRangeHeaders(
ActionContext context,
RequestHeaders httpRequestHeaders,
long fileLength)
@@ -286,7 +301,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var (isRangeRequest, range) = RangeHelper.ParseRange(
context.HttpContext,
httpRequestHeaders,
- fileLength);
+ fileLength,
+ Logger);
if (!isRangeRequest)
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileStreamResultExecutor.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileStreamResultExecutor.cs
index 72a4d53cb5..46e10e036c 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileStreamResultExecutor.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileStreamResultExecutor.cs
@@ -3,6 +3,7 @@
using System;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@@ -28,6 +29,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
throw new ArgumentNullException(nameof(result));
}
+ Logger.ExecutingFileResult(result);
+
long? fileLength = null;
if (result.FileStream.CanSeek)
{
@@ -67,6 +70,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
return Task.CompletedTask;
}
+ if (range != null)
+ {
+ Logger.WritingRangeToBody();
+ }
+
return WriteFileAsync(context.HttpContext, result.FileStream, range, rangeLength);
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/PhysicalFileResultExecutor.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/PhysicalFileResultExecutor.cs
index e36d22a3d9..fa4757212d 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/PhysicalFileResultExecutor.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/PhysicalFileResultExecutor.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Core;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@@ -39,6 +40,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
}
+ Logger.ExecutingFileResult(result, result.FileName);
+
var lastModified = result.LastModified ?? fileInfo.LastModified;
var (range, rangeLength, serveBody) = SetHeadersAndLog(
context,
@@ -79,6 +82,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
throw new NotSupportedException(Resources.FormatFileResult_PathNotRooted(result.FileName));
}
+ if (range != null)
+ {
+ Logger.WritingRangeToBody();
+ }
+
var sendFile = response.HttpContext.Features.Get();
if (sendFile != null)
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/VirtualFileResultExecutor.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/VirtualFileResultExecutor.cs
index 42b0b2a669..d9702a572f 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/VirtualFileResultExecutor.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/VirtualFileResultExecutor.cs
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Core;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@@ -49,6 +50,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
}
+ Logger.ExecutingFileResult(result, result.FileName);
+
var lastModified = result.LastModified ?? fileInfo.LastModified;
var (range, rangeLength, serveBody) = SetHeadersAndLog(
context,
@@ -85,6 +88,12 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var response = context.HttpContext.Response;
var physicalPath = fileInfo.PhysicalPath;
+
+ if (range != null)
+ {
+ Logger.WritingRangeToBody();
+ }
+
var sendFile = response.HttpContext.Features.Get();
if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
{
@@ -110,6 +119,10 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
private IFileInfo GetFileInformation(VirtualFileResult result)
{
var fileProvider = GetFileProvider(result);
+ if (fileProvider is NullFileProvider)
+ {
+ throw new InvalidOperationException(Resources.VirtualFileResultExecutor_NoFileProviderConfigured);
+ }
var normalizedPath = result.FileName;
if (normalizedPath.StartsWith("~", StringComparison.Ordinal))
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreLoggerExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreLoggerExtensions.cs
index ed5e2354e7..4cc0f8f900 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreLoggerExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreLoggerExtensions.cs
@@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Formatters.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Internal
{
@@ -45,8 +46,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private static readonly Action _ambiguousActions;
private static readonly Action _constraintMismatch;
- private static readonly Action _fileResultExecuting;
-
+ private static readonly Action _executingFileResult;
+ private static readonly Action _executingFileResultWithNoFileName;
+ private static readonly Action _notEnabledForRangeProcessing;
+ private static readonly Action _writingRangeToBody;
private static readonly Action _authorizationFailure;
private static readonly Action _resourceFilterShortCircuit;
private static readonly Action _resultFilterShortCircuit;
@@ -101,6 +104,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private static readonly Action _cannotApplyFormatFilterContentType;
private static readonly Action _actionDoesNotExplicitlySpecifyContentTypes;
private static readonly Action, Exception> _selectingOutputFormatterUsingAcceptHeader;
+ private static readonly Action _ifMatchPreconditionFailed;
+ private static readonly Action _ifUnmodifiedSincePreconditionFailed;
+ private static readonly Action _ifRangeLastModifiedPreconditionFailed;
+ private static readonly Action _ifRangeETagPreconditionFailed;
private static readonly Action, MediaTypeCollection, Exception> _selectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes;
private static readonly Action _selectingOutputFormatterWithoutUsingContentTypes;
private static readonly Action _selectingOutputFormatterUsingContentTypes;
@@ -175,10 +182,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
2,
"Action '{ActionName}' with id '{ActionId}' did not match the constraint '{ActionConstraint}'");
- _fileResultExecuting = LoggerMessage.Define(
+ _executingFileResult = LoggerMessage.Define(
LogLevel.Information,
1,
- "Executing FileResult, sending file as {FileDownloadName}");
+ "Executing {FileResultType}, sending file '{FileDownloadPath}' with download name '{FileDownloadName}' ...");
+
+ _executingFileResultWithNoFileName = LoggerMessage.Define(
+ LogLevel.Information,
+ 2,
+ "Executing {FileResultType}, sending file with download name '{FileDownloadName}' ...");
_authorizationFailure = LoggerMessage.Define