File results - logging
This commit is contained in:
parent
997544077a
commit
40d027fca3
|
|
@ -1551,7 +1551,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
|
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
|
||||||
[NonAction]
|
[NonAction]
|
||||||
|
|
@ -1564,7 +1564,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
|
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
|
||||||
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
|
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
|
||||||
|
|
@ -1579,7 +1579,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="fileDownloadName">The suggested file name.</param>
|
/// <param name="fileDownloadName">The suggested file name.</param>
|
||||||
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
|
/// <returns>The created <see cref="PhysicalFileResult"/> for the response.</returns>
|
||||||
|
|
@ -1597,7 +1597,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="fileDownloadName">The suggested file name.</param>
|
/// <param name="fileDownloadName">The suggested file name.</param>
|
||||||
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
|
/// <param name="enableRangeProcessing">Set to <c>true</c> to enable range requests processing.</param>
|
||||||
|
|
@ -1620,7 +1620,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
||||||
/// <param name="entityTag">The <see cref="EntityTagHeaderValue"/> associated with the file.</param>
|
/// <param name="entityTag">The <see cref="EntityTagHeaderValue"/> associated with the file.</param>
|
||||||
|
|
@ -1641,7 +1641,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
||||||
/// <param name="entityTag">The <see cref="EntityTagHeaderValue"/> associated with the file.</param>
|
/// <param name="entityTag">The <see cref="EntityTagHeaderValue"/> associated with the file.</param>
|
||||||
|
|
@ -1664,7 +1664,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="fileDownloadName">The suggested file name.</param>
|
/// <param name="fileDownloadName">The suggested file name.</param>
|
||||||
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
||||||
|
|
@ -1687,7 +1687,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
/// This supports range requests (<see cref="StatusCodes.Status206PartialContent"/> or
|
||||||
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
/// <see cref="StatusCodes.Status416RangeNotSatisfiable"/> if the range is not satisfiable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalPath">The physical path of the file to be returned.</param>
|
/// <param name="physicalPath">The path to the file. The path must be an absolute path.</param>
|
||||||
/// <param name="contentType">The Content-Type of the file.</param>
|
/// <param name="contentType">The Content-Type of the file.</param>
|
||||||
/// <param name="fileDownloadName">The suggested file name.</param>
|
/// <param name="fileDownloadName">The suggested file name.</param>
|
||||||
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
/// <param name="lastModified">The <see cref="DateTimeOffset"/> of when the file was last modified.</param>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -29,6 +30,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
throw new ArgumentNullException(nameof(result));
|
throw new ArgumentNullException(nameof(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.ExecutingFileResult(result);
|
||||||
|
|
||||||
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
||||||
context,
|
context,
|
||||||
result,
|
result,
|
||||||
|
|
@ -62,6 +65,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
|
Logger.WritingRangeToBody();
|
||||||
|
}
|
||||||
|
|
||||||
var fileContentStream = new MemoryStream(result.FileContents);
|
var fileContentStream = new MemoryStream(result.FileContents);
|
||||||
return WriteFileAsync(context.HttpContext, fileContentStream, range, rangeLength);
|
return WriteFileAsync(context.HttpContext, fileContentStream, range, rangeLength);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
|
|
||||||
SetContentType(context, result);
|
SetContentType(context, result);
|
||||||
SetContentDispositionHeader(context, result);
|
SetContentDispositionHeader(context, result);
|
||||||
Logger.FileResultExecuting(result.FileDownloadName);
|
|
||||||
|
|
||||||
var request = context.HttpContext.Request;
|
var request = context.HttpContext.Request;
|
||||||
var httpRequestHeaders = request.GetTypedHeaders();
|
var httpRequestHeaders = request.GetTypedHeaders();
|
||||||
|
|
@ -106,6 +105,10 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
|
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.NotEnabledForRangeProcessing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (range: null, rangeLength: 0, serveBody);
|
return (range: null, rangeLength: 0, serveBody);
|
||||||
|
|
@ -150,7 +153,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
response.Headers[HeaderNames.AcceptRanges] = AcceptRangeHeaderValue;
|
response.Headers[HeaderNames.AcceptRanges] = AcceptRangeHeaderValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IfRangeValid(
|
internal bool IfRangeValid(
|
||||||
RequestHeaders httpRequestHeaders,
|
RequestHeaders httpRequestHeaders,
|
||||||
DateTimeOffset? lastModified = null,
|
DateTimeOffset? lastModified = null,
|
||||||
EntityTagHeaderValue etag = null)
|
EntityTagHeaderValue etag = null)
|
||||||
|
|
@ -168,11 +171,13 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
{
|
{
|
||||||
if (lastModified.HasValue && lastModified > ifRange.LastModified)
|
if (lastModified.HasValue && lastModified > ifRange.LastModified)
|
||||||
{
|
{
|
||||||
|
Logger.IfRangeLastModifiedPreconditionFailed(lastModified, ifRange.LastModified);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (etag != null && ifRange.EntityTag != null && !ifRange.EntityTag.Compare(etag, useStrongComparison: true))
|
else if (etag != null && ifRange.EntityTag != null && !ifRange.EntityTag.Compare(etag, useStrongComparison: true))
|
||||||
{
|
{
|
||||||
|
Logger.IfRangeETagPreconditionFailed(etag, ifRange.EntityTag);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +186,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
internal static PreconditionState GetPreconditionState(
|
internal PreconditionState GetPreconditionState(
|
||||||
RequestHeaders httpRequestHeaders,
|
RequestHeaders httpRequestHeaders,
|
||||||
DateTimeOffset? lastModified = null,
|
DateTimeOffset? lastModified = null,
|
||||||
EntityTagHeaderValue etag = null)
|
EntityTagHeaderValue etag = null)
|
||||||
|
|
@ -200,6 +205,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
etag: etag,
|
etag: etag,
|
||||||
matchFoundState: PreconditionState.ShouldProcess,
|
matchFoundState: PreconditionState.ShouldProcess,
|
||||||
matchNotFoundState: PreconditionState.PreconditionFailed);
|
matchNotFoundState: PreconditionState.PreconditionFailed);
|
||||||
|
|
||||||
|
if (ifMatchState == PreconditionState.PreconditionFailed)
|
||||||
|
{
|
||||||
|
Logger.IfMatchPreconditionFailed(etag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 14.26 If-None-Match
|
// 14.26 If-None-Match
|
||||||
|
|
@ -229,6 +239,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
{
|
{
|
||||||
var unmodified = ifUnmodifiedSince >= lastModified;
|
var unmodified = ifUnmodifiedSince >= lastModified;
|
||||||
ifUnmodifiedSinceState = unmodified ? PreconditionState.ShouldProcess : PreconditionState.PreconditionFailed;
|
ifUnmodifiedSinceState = unmodified ? PreconditionState.ShouldProcess : PreconditionState.PreconditionFailed;
|
||||||
|
|
||||||
|
if (ifUnmodifiedSinceState == PreconditionState.PreconditionFailed)
|
||||||
|
{
|
||||||
|
Logger.IfUnmodifiedSincePreconditionFailed(lastModified, ifUnmodifiedSince);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var state = GetMaxPreconditionState(ifMatchState, ifNoneMatchState, ifModifiedSinceState, ifUnmodifiedSinceState);
|
var state = GetMaxPreconditionState(ifMatchState, ifNoneMatchState, ifModifiedSinceState, ifUnmodifiedSinceState);
|
||||||
|
|
@ -273,7 +288,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetRangeHeaders(
|
private (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetRangeHeaders(
|
||||||
ActionContext context,
|
ActionContext context,
|
||||||
RequestHeaders httpRequestHeaders,
|
RequestHeaders httpRequestHeaders,
|
||||||
long fileLength)
|
long fileLength)
|
||||||
|
|
@ -286,7 +301,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
var (isRangeRequest, range) = RangeHelper.ParseRange(
|
var (isRangeRequest, range) = RangeHelper.ParseRange(
|
||||||
context.HttpContext,
|
context.HttpContext,
|
||||||
httpRequestHeaders,
|
httpRequestHeaders,
|
||||||
fileLength);
|
fileLength,
|
||||||
|
Logger);
|
||||||
|
|
||||||
if (!isRangeRequest)
|
if (!isRangeRequest)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -28,6 +29,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
throw new ArgumentNullException(nameof(result));
|
throw new ArgumentNullException(nameof(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.ExecutingFileResult(result);
|
||||||
|
|
||||||
long? fileLength = null;
|
long? fileLength = null;
|
||||||
if (result.FileStream.CanSeek)
|
if (result.FileStream.CanSeek)
|
||||||
{
|
{
|
||||||
|
|
@ -67,6 +70,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
|
Logger.WritingRangeToBody();
|
||||||
|
}
|
||||||
|
|
||||||
return WriteFileAsync(context.HttpContext, result.FileStream, range, rangeLength);
|
return WriteFileAsync(context.HttpContext, result.FileStream, range, rangeLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Mvc.Core;
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -39,6 +40,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
|
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.ExecutingFileResult(result, result.FileName);
|
||||||
|
|
||||||
var lastModified = result.LastModified ?? fileInfo.LastModified;
|
var lastModified = result.LastModified ?? fileInfo.LastModified;
|
||||||
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
||||||
context,
|
context,
|
||||||
|
|
@ -79,6 +82,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
throw new NotSupportedException(Resources.FormatFileResult_PathNotRooted(result.FileName));
|
throw new NotSupportedException(Resources.FormatFileResult_PathNotRooted(result.FileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
|
Logger.WritingRangeToBody();
|
||||||
|
}
|
||||||
|
|
||||||
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
||||||
if (sendFile != null)
|
if (sendFile != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Mvc.Core;
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
@ -49,6 +50,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
|
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.ExecutingFileResult(result, result.FileName);
|
||||||
|
|
||||||
var lastModified = result.LastModified ?? fileInfo.LastModified;
|
var lastModified = result.LastModified ?? fileInfo.LastModified;
|
||||||
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
||||||
context,
|
context,
|
||||||
|
|
@ -85,6 +88,12 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
|
|
||||||
var response = context.HttpContext.Response;
|
var response = context.HttpContext.Response;
|
||||||
var physicalPath = fileInfo.PhysicalPath;
|
var physicalPath = fileInfo.PhysicalPath;
|
||||||
|
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
|
Logger.WritingRangeToBody();
|
||||||
|
}
|
||||||
|
|
||||||
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
||||||
if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
|
if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
|
||||||
{
|
{
|
||||||
|
|
@ -110,6 +119,10 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
private IFileInfo GetFileInformation(VirtualFileResult result)
|
private IFileInfo GetFileInformation(VirtualFileResult result)
|
||||||
{
|
{
|
||||||
var fileProvider = GetFileProvider(result);
|
var fileProvider = GetFileProvider(result);
|
||||||
|
if (fileProvider is NullFileProvider)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(Resources.VirtualFileResultExecutor_NoFileProviderConfigured);
|
||||||
|
}
|
||||||
|
|
||||||
var normalizedPath = result.FileName;
|
var normalizedPath = result.FileName;
|
||||||
if (normalizedPath.StartsWith("~", StringComparison.Ordinal))
|
if (normalizedPath.StartsWith("~", StringComparison.Ordinal))
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters.Internal;
|
using Microsoft.AspNetCore.Mvc.Formatters.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -45,8 +46,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
private static readonly Action<ILogger, string, Exception> _ambiguousActions;
|
private static readonly Action<ILogger, string, Exception> _ambiguousActions;
|
||||||
private static readonly Action<ILogger, string, string, IActionConstraint, Exception> _constraintMismatch;
|
private static readonly Action<ILogger, string, string, IActionConstraint, Exception> _constraintMismatch;
|
||||||
|
|
||||||
private static readonly Action<ILogger, string, Exception> _fileResultExecuting;
|
private static readonly Action<ILogger, FileResult, string, string, Exception> _executingFileResult;
|
||||||
|
private static readonly Action<ILogger, FileResult, string, Exception> _executingFileResultWithNoFileName;
|
||||||
|
private static readonly Action<ILogger, Exception> _notEnabledForRangeProcessing;
|
||||||
|
private static readonly Action<ILogger, Exception> _writingRangeToBody;
|
||||||
private static readonly Action<ILogger, object, Exception> _authorizationFailure;
|
private static readonly Action<ILogger, object, Exception> _authorizationFailure;
|
||||||
private static readonly Action<ILogger, object, Exception> _resourceFilterShortCircuit;
|
private static readonly Action<ILogger, object, Exception> _resourceFilterShortCircuit;
|
||||||
private static readonly Action<ILogger, object, Exception> _resultFilterShortCircuit;
|
private static readonly Action<ILogger, object, Exception> _resultFilterShortCircuit;
|
||||||
|
|
@ -101,6 +104,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
private static readonly Action<ILogger, string, Exception> _cannotApplyFormatFilterContentType;
|
private static readonly Action<ILogger, string, Exception> _cannotApplyFormatFilterContentType;
|
||||||
private static readonly Action<ILogger, Exception> _actionDoesNotExplicitlySpecifyContentTypes;
|
private static readonly Action<ILogger, Exception> _actionDoesNotExplicitlySpecifyContentTypes;
|
||||||
private static readonly Action<ILogger, IEnumerable<MediaTypeSegmentWithQuality>, Exception> _selectingOutputFormatterUsingAcceptHeader;
|
private static readonly Action<ILogger, IEnumerable<MediaTypeSegmentWithQuality>, Exception> _selectingOutputFormatterUsingAcceptHeader;
|
||||||
|
private static readonly Action<ILogger, EntityTagHeaderValue, Exception> _ifMatchPreconditionFailed;
|
||||||
|
private static readonly Action<ILogger, DateTimeOffset?, DateTimeOffset?, Exception> _ifUnmodifiedSincePreconditionFailed;
|
||||||
|
private static readonly Action<ILogger, DateTimeOffset?, DateTimeOffset?, Exception> _ifRangeLastModifiedPreconditionFailed;
|
||||||
|
private static readonly Action<ILogger, EntityTagHeaderValue, EntityTagHeaderValue, Exception> _ifRangeETagPreconditionFailed;
|
||||||
private static readonly Action<ILogger, IEnumerable<MediaTypeSegmentWithQuality>, MediaTypeCollection, Exception> _selectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes;
|
private static readonly Action<ILogger, IEnumerable<MediaTypeSegmentWithQuality>, MediaTypeCollection, Exception> _selectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes;
|
||||||
private static readonly Action<ILogger, Exception> _selectingOutputFormatterWithoutUsingContentTypes;
|
private static readonly Action<ILogger, Exception> _selectingOutputFormatterWithoutUsingContentTypes;
|
||||||
private static readonly Action<ILogger, MediaTypeCollection, Exception> _selectingOutputFormatterUsingContentTypes;
|
private static readonly Action<ILogger, MediaTypeCollection, Exception> _selectingOutputFormatterUsingContentTypes;
|
||||||
|
|
@ -175,10 +182,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
2,
|
2,
|
||||||
"Action '{ActionName}' with id '{ActionId}' did not match the constraint '{ActionConstraint}'");
|
"Action '{ActionName}' with id '{ActionId}' did not match the constraint '{ActionConstraint}'");
|
||||||
|
|
||||||
_fileResultExecuting = LoggerMessage.Define<string>(
|
_executingFileResult = LoggerMessage.Define<FileResult, string, string>(
|
||||||
LogLevel.Information,
|
LogLevel.Information,
|
||||||
1,
|
1,
|
||||||
"Executing FileResult, sending file as {FileDownloadName}");
|
"Executing {FileResultType}, sending file '{FileDownloadPath}' with download name '{FileDownloadName}' ...");
|
||||||
|
|
||||||
|
_executingFileResultWithNoFileName = LoggerMessage.Define<FileResult, string>(
|
||||||
|
LogLevel.Information,
|
||||||
|
2,
|
||||||
|
"Executing {FileResultType}, sending file with download name '{FileDownloadName}' ...");
|
||||||
|
|
||||||
_authorizationFailure = LoggerMessage.Define<object>(
|
_authorizationFailure = LoggerMessage.Define<object>(
|
||||||
LogLevel.Information,
|
LogLevel.Information,
|
||||||
|
|
@ -404,6 +416,36 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
11,
|
11,
|
||||||
"List of registered output formatters, in the following order: {OutputFormatters}");
|
"List of registered output formatters, in the following order: {OutputFormatters}");
|
||||||
|
|
||||||
|
_ifMatchPreconditionFailed = LoggerMessage.Define<EntityTagHeaderValue>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
12,
|
||||||
|
"Current request's If-Match header check failed as the file's current etag '{CurrentETag}' does not match with any of the supplied etags.");
|
||||||
|
|
||||||
|
_ifUnmodifiedSincePreconditionFailed = LoggerMessage.Define<DateTimeOffset?, DateTimeOffset?>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
13,
|
||||||
|
"Current request's If-Unmodified-Since header check failed as the file was modified (at '{lastModified}') after the If-Unmodified-Since date '{IfUnmodifiedSinceDate}'.");
|
||||||
|
|
||||||
|
_ifRangeLastModifiedPreconditionFailed = LoggerMessage.Define<DateTimeOffset?, DateTimeOffset?>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
14,
|
||||||
|
"Could not serve range as the file was modified (at {LastModified}) after the if-Range's last modified date '{IfRangeLastModified}'.");
|
||||||
|
|
||||||
|
_ifRangeETagPreconditionFailed = LoggerMessage.Define<EntityTagHeaderValue, EntityTagHeaderValue>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
15,
|
||||||
|
"Could not serve range as the file's current etag '{CurrentETag}' does not match the If-Range etag '{IfRangeETag}'.");
|
||||||
|
|
||||||
|
_notEnabledForRangeProcessing = LoggerMessage.Define(
|
||||||
|
LogLevel.Debug,
|
||||||
|
16,
|
||||||
|
$"The file result has not been enabled for processing range requests. To enable it, set the property '{nameof(FileResult.EnableRangeProcessing)}' on the result to 'true'.");
|
||||||
|
|
||||||
|
_writingRangeToBody = LoggerMessage.Define(
|
||||||
|
LogLevel.Debug,
|
||||||
|
17,
|
||||||
|
"Writing the requested range of bytes to the body...");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisteredOutputFormatters(this ILogger logger, IEnumerable<IOutputFormatter> outputFormatters)
|
public static void RegisteredOutputFormatters(this ILogger logger, IEnumerable<IOutputFormatter> outputFormatters)
|
||||||
|
|
@ -412,8 +454,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes(
|
public static void SelectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes(
|
||||||
this ILogger logger,
|
this ILogger logger,
|
||||||
IEnumerable<MediaTypeSegmentWithQuality> acceptHeader,
|
IEnumerable<MediaTypeSegmentWithQuality> acceptHeader,
|
||||||
MediaTypeCollection mediaTypeCollection)
|
MediaTypeCollection mediaTypeCollection)
|
||||||
{
|
{
|
||||||
_selectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes(logger, acceptHeader, mediaTypeCollection, null);
|
_selectingOutputFormatterUsingAcceptHeaderAndExplicitContentTypes(logger, acceptHeader, mediaTypeCollection, null);
|
||||||
|
|
@ -624,9 +666,24 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
_constraintMismatch(logger, actionName, actionId, actionConstraint, null);
|
_constraintMismatch(logger, actionName, actionId, actionConstraint, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void FileResultExecuting(this ILogger logger, string fileDownloadName)
|
public static void ExecutingFileResult(this ILogger logger, FileResult fileResult)
|
||||||
{
|
{
|
||||||
_fileResultExecuting(logger, fileDownloadName, null);
|
_executingFileResultWithNoFileName(logger, fileResult, fileResult.FileDownloadName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ExecutingFileResult(this ILogger logger, FileResult fileResult, string fileName)
|
||||||
|
{
|
||||||
|
_executingFileResult(logger, fileResult, fileName, fileResult.FileDownloadName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void NotEnabledForRangeProcessing(this ILogger logger)
|
||||||
|
{
|
||||||
|
_notEnabledForRangeProcessing(logger, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WritingRangeToBody(this ILogger logger)
|
||||||
|
{
|
||||||
|
_writingRangeToBody(logger, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AuthorizationFailure(
|
public static void AuthorizationFailure(
|
||||||
|
|
@ -881,6 +938,35 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void IfMatchPreconditionFailed(this ILogger logger, EntityTagHeaderValue etag)
|
||||||
|
{
|
||||||
|
_ifMatchPreconditionFailed(logger, etag, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void IfUnmodifiedSincePreconditionFailed(
|
||||||
|
this ILogger logger,
|
||||||
|
DateTimeOffset? lastModified,
|
||||||
|
DateTimeOffset? ifUnmodifiedSinceDate)
|
||||||
|
{
|
||||||
|
_ifUnmodifiedSincePreconditionFailed(logger, lastModified, ifUnmodifiedSinceDate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void IfRangeLastModifiedPreconditionFailed(
|
||||||
|
this ILogger logger,
|
||||||
|
DateTimeOffset? lastModified,
|
||||||
|
DateTimeOffset? ifRangeLastModifiedDate)
|
||||||
|
{
|
||||||
|
_ifRangeLastModifiedPreconditionFailed(logger, lastModified, ifRangeLastModifiedDate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void IfRangeETagPreconditionFailed(
|
||||||
|
this ILogger logger,
|
||||||
|
EntityTagHeaderValue currentETag,
|
||||||
|
EntityTagHeaderValue ifRangeTag)
|
||||||
|
{
|
||||||
|
_ifRangeETagPreconditionFailed(logger, currentETag, ifRangeTag, null);
|
||||||
|
}
|
||||||
|
|
||||||
private static void LogFilterExecutionPlan(
|
private static void LogFilterExecutionPlan(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
string filterType,
|
string filterType,
|
||||||
|
|
|
||||||
|
|
@ -1354,6 +1354,20 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
internal static string FormatApiController_AttributeRouteRequired(object p0)
|
internal static string FormatApiController_AttributeRouteRequired(object p0)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiController_AttributeRouteRequired"), p0);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiController_AttributeRouteRequired"), p0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No file provider has been configured to process the supplied file.
|
||||||
|
/// </summary>
|
||||||
|
internal static string VirtualFileResultExecutor_NoFileProviderConfigured
|
||||||
|
{
|
||||||
|
get => GetString("VirtualFileResultExecutor_NoFileProviderConfigured");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No file provider has been configured to process the supplied file.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatVirtualFileResultExecutor_NoFileProviderConfigured()
|
||||||
|
=> GetString("VirtualFileResultExecutor_NoFileProviderConfigured");
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -418,4 +418,7 @@
|
||||||
<data name="ApiController_AttributeRouteRequired" xml:space="preserve">
|
<data name="ApiController_AttributeRouteRequired" xml:space="preserve">
|
||||||
<value>Action methods on controllers annotated with {0} must have an attribute route.</value>
|
<value>Action methods on controllers annotated with {0} must have an attribute route.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="VirtualFileResultExecutor_NoFileProviderConfigured" xml:space="preserve">
|
||||||
|
<value>No file provider has been configured to process the supplied file.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -142,28 +142,6 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
await Assert.ThrowsAsync<InvalidOperationException>(() => result.ExecuteResultAsync(actionContext));
|
await Assert.ThrowsAsync<InvalidOperationException>(() => result.ExecuteResultAsync(actionContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
|
||||||
public async Task ExecuteResultAsync_LogsInformation_IfCanResolveLoggerFactory()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
var services = new ServiceCollection();
|
|
||||||
var loggerSink = new TestSink();
|
|
||||||
services.AddSingleton<ILoggerFactory>(new TestLoggerFactory(loggerSink, true));
|
|
||||||
services.AddSingleton<EmptyFileResultExecutor>();
|
|
||||||
httpContext.RequestServices = services.BuildServiceProvider();
|
|
||||||
|
|
||||||
var actionContext = CreateActionContext(httpContext);
|
|
||||||
var result = new EmptyFileResult("application/my-type");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await result.ExecuteResultAsync(actionContext);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Single(loggerSink.Writes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TheoryData<string, string> ContentDispositionData
|
public static TheoryData<string, string> ContentDispositionData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -302,9 +280,10 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
httpRequestHeaders.IfUnmodifiedSince = lastModified;
|
httpRequestHeaders.IfUnmodifiedSince = lastModified;
|
||||||
httpRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(1);
|
httpRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(1);
|
||||||
actionContext.HttpContext = httpContext;
|
actionContext.HttpContext = httpContext;
|
||||||
|
var fileResult = (new Mock<FileResultExecutorBase>(NullLogger.Instance)).Object;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var state = FileResultExecutorBase.GetPreconditionState(
|
var state = fileResult.GetPreconditionState(
|
||||||
httpRequestHeaders,
|
httpRequestHeaders,
|
||||||
lastModified,
|
lastModified,
|
||||||
etag);
|
etag);
|
||||||
|
|
@ -338,9 +317,10 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
httpRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue;
|
httpRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue;
|
||||||
httpRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(2);
|
httpRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(2);
|
||||||
actionContext.HttpContext = httpContext;
|
actionContext.HttpContext = httpContext;
|
||||||
|
var fileResult = (new Mock<FileResultExecutorBase>(NullLogger.Instance)).Object;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var state = FileResultExecutorBase.GetPreconditionState(
|
var state = fileResult.GetPreconditionState(
|
||||||
httpRequestHeaders,
|
httpRequestHeaders,
|
||||||
lastModified,
|
lastModified,
|
||||||
etag);
|
etag);
|
||||||
|
|
@ -373,9 +353,10 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
};
|
};
|
||||||
httpRequestHeaders.IfModifiedSince = lastModified;
|
httpRequestHeaders.IfModifiedSince = lastModified;
|
||||||
actionContext.HttpContext = httpContext;
|
actionContext.HttpContext = httpContext;
|
||||||
|
var fileResult = (new Mock<FileResultExecutorBase>(NullLogger.Instance)).Object;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var state = FileResultExecutorBase.GetPreconditionState(
|
var state = fileResult.GetPreconditionState(
|
||||||
httpRequestHeaders,
|
httpRequestHeaders,
|
||||||
lastModified,
|
lastModified,
|
||||||
etag);
|
etag);
|
||||||
|
|
@ -400,9 +381,10 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
httpRequestHeaders.IfRange = new RangeConditionHeaderValue(ifRangeString);
|
httpRequestHeaders.IfRange = new RangeConditionHeaderValue(ifRangeString);
|
||||||
httpRequestHeaders.IfModifiedSince = lastModified;
|
httpRequestHeaders.IfModifiedSince = lastModified;
|
||||||
actionContext.HttpContext = httpContext;
|
actionContext.HttpContext = httpContext;
|
||||||
|
var fileResult = (new Mock<FileResultExecutorBase>(NullLogger.Instance)).Object;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var ifRangeIsValid = FileResultExecutorBase.IfRangeValid(
|
var ifRangeIsValid = fileResult.IfRangeValid(
|
||||||
httpRequestHeaders,
|
httpRequestHeaders,
|
||||||
lastModified,
|
lastModified,
|
||||||
etag);
|
etag);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue