[Fixes #4207] Review IActionResult classes and add facades as necessary.
* Moved FileResults to use executor façades * Changed redirect result types to use executor façades.
This commit is contained in:
parent
13a3bbaeb6
commit
8493064fe5
|
|
@ -207,6 +207,14 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.TryAddSingleton(ArrayPool<byte>.Shared);
|
||||
services.TryAddSingleton(ArrayPool<char>.Shared);
|
||||
services.TryAddSingleton<ObjectResultExecutor>();
|
||||
services.TryAddSingleton<PhysicalFileResultExecutor>();
|
||||
services.TryAddSingleton<VirtualFileResultExecutor>();
|
||||
services.TryAddSingleton<FileStreamResultExecutor>();
|
||||
services.TryAddSingleton<FileContentResultExecutor>();
|
||||
services.TryAddSingleton<RedirectResultExecutor>();
|
||||
services.TryAddSingleton<LocalRedirectResultExecutor>();
|
||||
services.TryAddSingleton<RedirectToActionResultExecutor>();
|
||||
services.TryAddSingleton<RedirectToRouteResultExecutor>();
|
||||
|
||||
//
|
||||
// Setup default handler
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -72,12 +74,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task WriteFileAsync(HttpResponse response)
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>();
|
||||
bufferingFeature?.DisableResponseBuffering();
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
return response.Body.WriteAsync(FileContents, offset: 0, count: FileContents.Length);
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<FileContentResultExecutor>();
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,6 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -47,44 +43,5 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
get { return _fileDownloadName ?? string.Empty; }
|
||||
set { _fileDownloadName = value; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<FileResult>();
|
||||
|
||||
var response = context.HttpContext.Response;
|
||||
response.ContentType = ContentType.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(FileDownloadName))
|
||||
{
|
||||
// From RFC 2183, Sec. 2.3:
|
||||
// The sender may want to suggest a filename to be used if the entity is
|
||||
// detached and stored in a separate file. If the receiving MUA writes
|
||||
// the entity to a file, the suggested filename should be used as a
|
||||
// basis for the actual filename, where possible.
|
||||
var contentDisposition = new ContentDispositionHeaderValue("attachment");
|
||||
contentDisposition.SetHttpFileName(FileDownloadName);
|
||||
context.HttpContext.Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
|
||||
}
|
||||
|
||||
logger.FileResultExecuting(FileDownloadName);
|
||||
return WriteFileAsync(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the file to the specified <paramref name="response"/>.
|
||||
/// </summary>
|
||||
/// <param name="response">The <see cref="HttpResponse"/>.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task"/> that will complete when the file has been written to the response.
|
||||
/// </returns>
|
||||
protected abstract Task WriteFileAsync(HttpResponse response);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -17,9 +16,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// </summary>
|
||||
public class FileStreamResult : FileResult
|
||||
{
|
||||
// default buffer size as defined in BufferedStream type
|
||||
private const int BufferSize = 0x1000;
|
||||
|
||||
private Stream _fileStream;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -73,17 +69,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected async override Task WriteFileAsync(HttpResponse response)
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
var outputStream = response.Body;
|
||||
|
||||
using (FileStream)
|
||||
if (context == null)
|
||||
{
|
||||
var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>();
|
||||
bufferingFeature?.DisableResponseBuffering();
|
||||
|
||||
await FileStream.CopyToAsync(outputStream, BufferSize);
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<FileStreamResultExecutor>();
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class FileContentResultExecutor : FileResultExecutorBase
|
||||
{
|
||||
public FileContentResultExecutor(ILoggerFactory loggerFactory)
|
||||
: base(CreateLogger<FileContentResultExecutor>(loggerFactory))
|
||||
{
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(ActionContext context, FileContentResult result)
|
||||
{
|
||||
SetHeadersAndLog(context, result);
|
||||
return WriteFileAsync(context, result);
|
||||
}
|
||||
|
||||
private static Task WriteFileAsync(ActionContext context, FileContentResult result)
|
||||
{
|
||||
var response = context.HttpContext.Response;
|
||||
|
||||
var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>();
|
||||
bufferingFeature?.DisableResponseBuffering();
|
||||
|
||||
return response.Body.WriteAsync(result.FileContents, offset: 0, count: result.FileContents.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class FileResultExecutorBase
|
||||
{
|
||||
public FileResultExecutorBase(ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected void SetHeadersAndLog(ActionContext context, FileResult result)
|
||||
{
|
||||
SetContentType(context, result);
|
||||
SetContentDispositionHeader(context, result);
|
||||
Logger.FileResultExecuting(result.FileDownloadName);
|
||||
}
|
||||
|
||||
private void SetContentDispositionHeader(ActionContext context, FileResult result)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(result.FileDownloadName))
|
||||
{
|
||||
// From RFC 2183, Sec. 2.3:
|
||||
// The sender may want to suggest a filename to be used if the entity is
|
||||
// detached and stored in a separate file. If the receiving MUA writes
|
||||
// the entity to a file, the suggested filename should be used as a
|
||||
// basis for the actual filename, where possible.
|
||||
var contentDisposition = new ContentDispositionHeaderValue("attachment");
|
||||
contentDisposition.SetHttpFileName(result.FileDownloadName);
|
||||
context.HttpContext.Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetContentType(ActionContext context, FileResult result)
|
||||
{
|
||||
var response = context.HttpContext.Response;
|
||||
response.ContentType = result.ContentType.ToString();
|
||||
}
|
||||
|
||||
protected static ILogger CreateLogger<T>(ILoggerFactory factory)
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
return factory.CreateLogger<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class FileStreamResultExecutor : FileResultExecutorBase
|
||||
{
|
||||
// default buffer size as defined in BufferedStream type
|
||||
private const int BufferSize = 0x1000;
|
||||
|
||||
public FileStreamResultExecutor(ILoggerFactory loggerFactory)
|
||||
: base(CreateLogger<VirtualFileResultExecutor>(loggerFactory))
|
||||
{
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(ActionContext context, FileStreamResult result)
|
||||
{
|
||||
SetHeadersAndLog(context, result);
|
||||
return WriteFileAsync(context, result);
|
||||
}
|
||||
|
||||
private static async Task WriteFileAsync(ActionContext context, FileStreamResult result)
|
||||
{
|
||||
var response = context.HttpContext.Response;
|
||||
var outputStream = response.Body;
|
||||
|
||||
using (result.FileStream)
|
||||
{
|
||||
var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>();
|
||||
bufferingFeature?.DisableResponseBuffering();
|
||||
|
||||
await result.FileStream.CopyToAsync(outputStream, BufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class LocalRedirectResultExecutor
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||
|
||||
public LocalRedirectResultExecutor(ILoggerFactory loggerFactory, IUrlHelperFactory urlHelperFactory)
|
||||
{
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
if (urlHelperFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(urlHelperFactory));
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger<LocalRedirectResultExecutor>();
|
||||
_urlHelperFactory = urlHelperFactory;
|
||||
}
|
||||
|
||||
public void Execute(ActionContext context, LocalRedirectResult result)
|
||||
{
|
||||
var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context);
|
||||
|
||||
// IsLocalUrl is called to handle Urls starting with '~/'.
|
||||
var destinationUrl = result.Url;
|
||||
if (!urlHelper.IsLocalUrl(result.Url))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.UrlNotLocal);
|
||||
}
|
||||
|
||||
destinationUrl = urlHelper.Content(result.Url);
|
||||
_logger.LocalRedirectResultExecuting(destinationUrl);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, result.Permanent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class PhysicalFileResultExecutor : FileResultExecutorBase
|
||||
{
|
||||
private const int DefaultBufferSize = 0x1000;
|
||||
|
||||
public PhysicalFileResultExecutor(ILoggerFactory loggerFactory)
|
||||
: base(CreateLogger<PhysicalFileResultExecutor>(loggerFactory))
|
||||
{
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(ActionContext context, PhysicalFileResult result)
|
||||
{
|
||||
SetHeadersAndLog(context, result);
|
||||
return WriteFileAsync(context, result);
|
||||
}
|
||||
|
||||
private async Task WriteFileAsync(ActionContext context, PhysicalFileResult result)
|
||||
{
|
||||
var response = context.HttpContext.Response;
|
||||
|
||||
if (!Path.IsPathRooted(result.FileName))
|
||||
{
|
||||
throw new NotSupportedException(Resources.FormatFileResult_PathNotRooted(result.FileName));
|
||||
}
|
||||
|
||||
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
||||
if (sendFile != null)
|
||||
{
|
||||
await sendFile.SendFileAsync(
|
||||
result.FileName,
|
||||
offset: 0,
|
||||
count: null,
|
||||
cancellation: default(CancellationToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileStream = GetFileStream(result.FileName);
|
||||
|
||||
using (fileStream)
|
||||
{
|
||||
await fileStream.CopyToAsync(response.Body, DefaultBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Stream GetFileStream(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
return new FileStream(
|
||||
path,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite,
|
||||
DefaultBufferSize,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class RedirectResultExecutor
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||
|
||||
public RedirectResultExecutor(ILoggerFactory loggerFactory, IUrlHelperFactory urlHelperFactory)
|
||||
{
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
if (urlHelperFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(urlHelperFactory));
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger<RedirectResultExecutor>();
|
||||
_urlHelperFactory = urlHelperFactory;
|
||||
}
|
||||
|
||||
public void Execute(ActionContext context, RedirectResult result)
|
||||
{
|
||||
var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context);
|
||||
|
||||
// IsLocalUrl is called to handle Urls starting with '~/'.
|
||||
var destinationUrl = result.Url;
|
||||
if (urlHelper.IsLocalUrl(destinationUrl))
|
||||
{
|
||||
destinationUrl = urlHelper.Content(result.Url);
|
||||
}
|
||||
|
||||
_logger.RedirectResultExecuting(destinationUrl);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, result.Permanent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class RedirectToActionResultExecutor
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||
|
||||
public RedirectToActionResultExecutor(ILoggerFactory loggerFactory, IUrlHelperFactory urlHelperFactory)
|
||||
{
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
if (urlHelperFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(urlHelperFactory));
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger<RedirectToActionResult>();
|
||||
_urlHelperFactory = urlHelperFactory;
|
||||
}
|
||||
|
||||
public void Execute(ActionContext context, RedirectToActionResult result)
|
||||
{
|
||||
var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context);
|
||||
|
||||
var destinationUrl = urlHelper.Action(result.ActionName, result.ControllerName, result.RouteValues);
|
||||
if (string.IsNullOrEmpty(destinationUrl))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.NoRoutesMatched);
|
||||
}
|
||||
|
||||
_logger.RedirectToActionResultExecuting(destinationUrl);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, result.Permanent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class RedirectToRouteResultExecutor
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||
|
||||
public RedirectToRouteResultExecutor(ILoggerFactory loggerFactory, IUrlHelperFactory urlHelperFactory)
|
||||
{
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
if (urlHelperFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(urlHelperFactory));
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger<RedirectToRouteResult>();
|
||||
_urlHelperFactory = urlHelperFactory;
|
||||
}
|
||||
|
||||
public void Execute(ActionContext context, RedirectToRouteResult result)
|
||||
{
|
||||
var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context);
|
||||
|
||||
var destinationUrl = urlHelper.RouteUrl(result.RouteName, result.RouteValues);
|
||||
if (string.IsNullOrEmpty(destinationUrl))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.NoRoutesMatched);
|
||||
}
|
||||
|
||||
_logger.RedirectToRouteResultExecuting(destinationUrl, result.RouteName);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, result.Permanent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class VirtualFileResultExecutor : FileResultExecutorBase
|
||||
{
|
||||
private const int DefaultBufferSize = 0x1000;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
public VirtualFileResultExecutor(ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment)
|
||||
: base(CreateLogger<VirtualFileResultExecutor>(loggerFactory))
|
||||
{
|
||||
if (hostingEnvironment == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||
}
|
||||
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(ActionContext context, VirtualFileResult result)
|
||||
{
|
||||
SetHeadersAndLog(context, result);
|
||||
return WriteFileAsync(context, result);
|
||||
}
|
||||
|
||||
private async Task WriteFileAsync(ActionContext context, VirtualFileResult result)
|
||||
{
|
||||
var response = context.HttpContext.Response;
|
||||
var fileProvider = GetFileProvider(result);
|
||||
|
||||
var normalizedPath = result.FileName;
|
||||
if (normalizedPath.StartsWith("~", StringComparison.Ordinal))
|
||||
{
|
||||
normalizedPath = normalizedPath.Substring(1);
|
||||
}
|
||||
|
||||
var fileInfo = fileProvider.GetFileInfo(normalizedPath);
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
var physicalPath = fileInfo.PhysicalPath;
|
||||
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
||||
if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
|
||||
{
|
||||
await sendFile.SendFileAsync(
|
||||
physicalPath,
|
||||
offset: 0,
|
||||
count: null,
|
||||
cancellation: default(CancellationToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileStream = GetFileStream(fileInfo);
|
||||
using (fileStream)
|
||||
{
|
||||
await fileStream.CopyToAsync(response.Body, DefaultBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
Resources.FormatFileResult_InvalidPath(result.FileName), result.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
private IFileProvider GetFileProvider(VirtualFileResult result)
|
||||
{
|
||||
if (result.FileProvider != null)
|
||||
{
|
||||
return result.FileProvider;
|
||||
}
|
||||
|
||||
result.FileProvider = _hostingEnvironment.WebRootFileProvider;
|
||||
|
||||
return result.FileProvider;
|
||||
}
|
||||
|
||||
protected virtual Stream GetFileStream(IFileInfo fileInfo)
|
||||
{
|
||||
return fileInfo.CreateReadStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Mvc.Core;
|
|||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -82,19 +81,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<LocalRedirectResult>();
|
||||
|
||||
var urlHelper = GetUrlHelper(context);
|
||||
|
||||
if (!urlHelper.IsLocalUrl(Url))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.UrlNotLocal);
|
||||
}
|
||||
|
||||
var destinationUrl = urlHelper.Content(Url);
|
||||
logger.LocalRedirectResultExecuting(destinationUrl);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, Permanent);
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<LocalRedirectResultExecutor>();
|
||||
executor.Execute(context, this);
|
||||
}
|
||||
|
||||
private IUrlHelper GetUrlHelper(ActionContext context)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -18,7 +15,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// </summary>
|
||||
public class PhysicalFileResult : FileResult
|
||||
{
|
||||
private const int DefaultBufferSize = 0x1000;
|
||||
private string _fileName;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -74,52 +70,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task WriteFileAsync(HttpResponse response)
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
if (!Path.IsPathRooted(FileName))
|
||||
if (context == null)
|
||||
{
|
||||
throw new NotSupportedException(Resources.FormatFileResult_PathNotRooted(FileName));
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
||||
if (sendFile != null)
|
||||
{
|
||||
await sendFile.SendFileAsync(
|
||||
FileName,
|
||||
offset: 0,
|
||||
count: null,
|
||||
cancellation: default(CancellationToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileStream = GetFileStream(FileName);
|
||||
|
||||
using (fileStream)
|
||||
{
|
||||
await fileStream.CopyToAsync(response.Body, DefaultBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="Stream"/> for the specified <paramref name="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path for which the <see cref="FileStream"/> is needed.</param>
|
||||
/// <returns><see cref="FileStream"/> for the specified <paramref name="path"/>.</returns>
|
||||
protected virtual Stream GetFileStream(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
return new FileStream(
|
||||
path,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite,
|
||||
DefaultBufferSize,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<PhysicalFileResultExecutor>();
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -68,32 +66,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<RedirectResult>();
|
||||
|
||||
var urlHelper = GetUrlHelper(context);
|
||||
|
||||
// IsLocalUrl is called to handle Urls starting with '~/'.
|
||||
var destinationUrl = Url;
|
||||
if (urlHelper.IsLocalUrl(destinationUrl))
|
||||
{
|
||||
destinationUrl = urlHelper.Content(Url);
|
||||
}
|
||||
|
||||
logger.RedirectResultExecuting(destinationUrl);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, Permanent);
|
||||
}
|
||||
|
||||
private IUrlHelper GetUrlHelper(ActionContext context)
|
||||
{
|
||||
var urlHelper = UrlHelper;
|
||||
if (urlHelper == null)
|
||||
{
|
||||
var services = context.HttpContext.RequestServices;
|
||||
urlHelper = services.GetRequiredService<IUrlHelperFactory>().GetUrlHelper(context);
|
||||
}
|
||||
|
||||
return urlHelper;
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<RedirectResultExecutor>();
|
||||
executor.Execute(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -64,31 +61,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<RedirectToActionResult>();
|
||||
|
||||
var urlHelper = GetUrlHelper(context);
|
||||
|
||||
var destinationUrl = urlHelper.Action(ActionName, ControllerName, RouteValues);
|
||||
if (string.IsNullOrEmpty(destinationUrl))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.NoRoutesMatched);
|
||||
}
|
||||
|
||||
logger.RedirectToActionResultExecuting(destinationUrl);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, Permanent);
|
||||
}
|
||||
|
||||
private IUrlHelper GetUrlHelper(ActionContext context)
|
||||
{
|
||||
var urlHelper = UrlHelper;
|
||||
if (urlHelper == null)
|
||||
{
|
||||
var services = context.HttpContext.RequestServices;
|
||||
urlHelper = services.GetRequiredService<IUrlHelperFactory>().GetUrlHelper(context);
|
||||
}
|
||||
|
||||
return urlHelper;
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<RedirectToActionResultExecutor>();
|
||||
executor.Execute(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
|
|
@ -61,31 +58,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<RedirectToRouteResult>();
|
||||
|
||||
var urlHelper = GetUrlHelper(context);
|
||||
|
||||
var destinationUrl = urlHelper.RouteUrl(RouteName, RouteValues);
|
||||
if (string.IsNullOrEmpty(destinationUrl))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.NoRoutesMatched);
|
||||
}
|
||||
|
||||
logger.RedirectToRouteResultExecuting(destinationUrl, RouteName);
|
||||
context.HttpContext.Response.Redirect(destinationUrl, Permanent);
|
||||
}
|
||||
|
||||
private IUrlHelper GetUrlHelper(ActionContext context)
|
||||
{
|
||||
var urlHelper = UrlHelper;
|
||||
if (urlHelper == null)
|
||||
{
|
||||
var services = context.HttpContext.RequestServices;
|
||||
urlHelper = services.GetRequiredService<IUrlHelperFactory>().GetUrlHelper(context);
|
||||
}
|
||||
|
||||
return urlHelper;
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<RedirectToRouteResultExecutor>();
|
||||
executor.Execute(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
|
@ -21,7 +18,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// </summary>
|
||||
public class VirtualFileResult : FileResult
|
||||
{
|
||||
private const int DefaultBufferSize = 0x1000;
|
||||
private string _fileName;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -83,71 +79,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public IFileProvider FileProvider { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task WriteFileAsync(HttpResponse response)
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
var fileProvider = GetFileProvider(response.HttpContext.RequestServices);
|
||||
|
||||
var normalizedPath = FileName;
|
||||
if (normalizedPath.StartsWith("~", StringComparison.Ordinal))
|
||||
if (context == null)
|
||||
{
|
||||
normalizedPath = normalizedPath.Substring(1);
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var fileInfo = fileProvider.GetFileInfo(normalizedPath);
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
var physicalPath = fileInfo.PhysicalPath;
|
||||
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
|
||||
if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
|
||||
{
|
||||
await sendFile.SendFileAsync(
|
||||
physicalPath,
|
||||
offset: 0,
|
||||
count: null,
|
||||
cancellation: default(CancellationToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileStream = GetFileStream(fileInfo);
|
||||
using (fileStream)
|
||||
{
|
||||
await fileStream.CopyToAsync(response.Body, DefaultBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
Resources.FormatFileResult_InvalidPath(FileName), FileName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="Stream"/> for the specified <paramref name="fileInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">The <see cref="IFileInfo"/> for which the stream is needed.</param>
|
||||
/// <returns><see cref="Stream"/> for the specified <paramref name="fileInfo"/>.</returns>
|
||||
protected virtual Stream GetFileStream(IFileInfo fileInfo)
|
||||
{
|
||||
if (fileInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileInfo));
|
||||
}
|
||||
|
||||
return fileInfo.CreateReadStream();
|
||||
}
|
||||
|
||||
private IFileProvider GetFileProvider(IServiceProvider requestServices)
|
||||
{
|
||||
if (FileProvider != null)
|
||||
{
|
||||
return FileProvider;
|
||||
}
|
||||
|
||||
var hostingEnvironment = requestServices.GetService<IHostingEnvironment>();
|
||||
FileProvider = hostingEnvironment.WebRootFileProvider;
|
||||
|
||||
return FileProvider;
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<VirtualFileResultExecutor>();
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -121,7 +122,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceCollection CreateServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddSingleton<FileContentResultExecutor>();
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
|
||||
return services;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.IO;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -78,15 +79,15 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public async Task ExecuteResultAsync_DoesNotSetContentDisposition_IfNotSpecified()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = new Mock<HttpContext>(MockBehavior.Strict);
|
||||
var provider = new ServiceCollection()
|
||||
.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance)
|
||||
.AddSingleton<EmptyFileResultExecutor>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
httpContext.SetupSet(c => c.Response.ContentType = "application/my-type").Verifiable();
|
||||
httpContext.Setup(c => c.Response.Body).Returns(Stream.Null);
|
||||
httpContext
|
||||
.Setup(c => c.RequestServices.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(NullLoggerFactory.Instance);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = provider;
|
||||
|
||||
var actionContext = CreateActionContext(httpContext.Object);
|
||||
var actionContext = CreateActionContext(httpContext);
|
||||
|
||||
var result = new EmptyFileResult("application/my-type");
|
||||
|
||||
|
|
@ -95,7 +96,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
// Assert
|
||||
Assert.True(result.WasWriteFileCalled);
|
||||
httpContext.Verify();
|
||||
Assert.Equal("application/my-type", httpContext.Response.ContentType);
|
||||
Assert.Equal(Stream.Null, httpContext.Response.Body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -140,6 +142,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
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);
|
||||
|
|
@ -249,6 +252,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceCollection CreateServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<EmptyFileResultExecutor>();
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
return services;
|
||||
}
|
||||
|
|
@ -282,9 +286,24 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
}
|
||||
|
||||
protected override Task WriteFileAsync(HttpResponse response)
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
WasWriteFileCalled = true;
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<EmptyFileResultExecutor>();
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyFileResultExecutor : FileResultExecutorBase
|
||||
{
|
||||
public EmptyFileResultExecutor(ILoggerFactory loggerFactory)
|
||||
:base(CreateLogger<EmptyFileResultExecutor>(loggerFactory))
|
||||
{
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(ActionContext context, EmptyFileResult result)
|
||||
{
|
||||
SetHeadersAndLog(context, result);
|
||||
result.WasWriteFileCalled = true;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -171,6 +172,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceCollection CreateServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<FileStreamResultExecutor>();
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -102,6 +103,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceProvider GetServiceProvider()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddSingleton<LocalRedirectResultExecutor>();
|
||||
serviceCollection.AddSingleton<IUrlHelperFactory, UrlHelperFactory>();
|
||||
serviceCollection.AddTransient<ILoggerFactory, LoggerFactory>();
|
||||
return serviceCollection.BuildServiceProvider();
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -28,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var path = Path.GetFullPath("helllo.txt");
|
||||
|
||||
// Act
|
||||
var result = new PhysicalFileResult(path, "text/plain");
|
||||
var result = new TestPhysicalFileResult(path, "text/plain");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(path, result.FileName);
|
||||
|
|
@ -43,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var expectedMediaType = contentType;
|
||||
|
||||
// Act
|
||||
var result = new PhysicalFileResult(path, contentType);
|
||||
var result = new TestPhysicalFileResult(path, contentType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(path, result.FileName);
|
||||
|
|
@ -75,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
// Arrange
|
||||
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
|
||||
var result = new PhysicalFileResult(path, "text/plain");
|
||||
var result = new TestPhysicalFileResult(path, "text/plain");
|
||||
var sendFileMock = new Mock<IHttpSendFileFeature>();
|
||||
sendFileMock
|
||||
.Setup(s => s.SendFileAsync(path, 0, null, CancellationToken.None))
|
||||
|
|
@ -204,6 +206,23 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
}
|
||||
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<TestPhysicalFileResultExecutor>();
|
||||
executor.IsAscii = IsAscii;
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
|
||||
public bool IsAscii { get; set; } = false;
|
||||
}
|
||||
|
||||
private class TestPhysicalFileResultExecutor : PhysicalFileResultExecutor
|
||||
{
|
||||
public TestPhysicalFileResultExecutor(ILoggerFactory loggerFactory)
|
||||
: base(loggerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsAscii { get; set; } = false;
|
||||
|
||||
protected override Stream GetFileStream(string path)
|
||||
|
|
@ -222,6 +241,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceCollection CreateServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<TestPhysicalFileResultExecutor>();
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -110,6 +111,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceProvider GetServiceProvider()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddSingleton<RedirectResultExecutor>();
|
||||
serviceCollection.AddSingleton<IUrlHelperFactory, UrlHelperFactory>();
|
||||
serviceCollection.AddTransient<ILoggerFactory, LoggerFactory>();
|
||||
return serviceCollection.BuildServiceProvider();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
|
|
@ -93,6 +94,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
private static IServiceCollection CreateServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<RedirectToActionResultExecutor>();
|
||||
services.AddSingleton<IUrlHelperFactory, UrlHelperFactory>();
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
|
|
@ -99,16 +100,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
factory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider
|
||||
.Setup(sp => sp.GetService(typeof(IUrlHelperFactory)))
|
||||
.Returns(factory.Object);
|
||||
serviceProvider
|
||||
.Setup(sp => sp.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(NullLoggerFactory.Instance);
|
||||
|
||||
var httpContext = GetHttpContext();
|
||||
httpContext.RequestServices = serviceProvider.Object;
|
||||
var httpContext = GetHttpContext(factory.Object);
|
||||
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var result = new RedirectToRouteResult(routeName, new { id = 10 });
|
||||
|
|
@ -123,9 +116,9 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Assert.Equal(locationUrl, httpContext.Response.Headers["Location"]);
|
||||
}
|
||||
|
||||
private static HttpContext GetHttpContext()
|
||||
private static HttpContext GetHttpContext(IUrlHelperFactory factory = null)
|
||||
{
|
||||
var services = CreateServices();
|
||||
var services = CreateServices(factory);
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = services.BuildServiceProvider();
|
||||
|
|
@ -133,9 +126,20 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
return httpContext;
|
||||
}
|
||||
|
||||
private static IServiceCollection CreateServices()
|
||||
private static IServiceCollection CreateServices(IUrlHelperFactory factory = null)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<RedirectToRouteResultExecutor>();
|
||||
|
||||
if (factory != null)
|
||||
{
|
||||
services.AddSingleton(factory);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IUrlHelperFactory, UrlHelperFactory>();
|
||||
}
|
||||
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
|
@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -29,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var path = Path.GetFullPath("helllo.txt");
|
||||
|
||||
// Act
|
||||
var result = new VirtualFileResult(path, "text/plain");
|
||||
var result = new TestVirtualFileResult(path, "text/plain");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(path, result.FileName);
|
||||
|
|
@ -44,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var expectedMediaType = contentType;
|
||||
|
||||
// Act
|
||||
var result = new VirtualFileResult(path, contentType);
|
||||
var result = new TestVirtualFileResult(path, contentType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(path, result.FileName);
|
||||
|
|
@ -65,7 +67,8 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var httpContext = GetHttpContext();
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
httpContext.RequestServices = new ServiceCollection()
|
||||
.AddSingleton<IHostingEnvironment>(appEnvironment.Object)
|
||||
.AddSingleton(appEnvironment.Object)
|
||||
.AddTransient<TestVirtualFileResultExecutor>()
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>()
|
||||
.BuildServiceProvider();
|
||||
var context = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
|
|
@ -244,7 +247,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
public async Task ExecuteResultAsync_WorksWithNonDiskBasedFiles()
|
||||
{
|
||||
// Arrange
|
||||
var httpContext = GetHttpContext();
|
||||
var httpContext = GetHttpContext(typeof(VirtualFileResultExecutor));
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var expectedData = "This is an embedded resource";
|
||||
|
|
@ -280,7 +283,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
fileInfo.SetupGet(f => f.Exists).Returns(false);
|
||||
var fileProvider = new Mock<IFileProvider>();
|
||||
fileProvider.Setup(f => f.GetFileInfo(path)).Returns(fileInfo.Object);
|
||||
var filePathResult = new VirtualFileResult(path, "text/plain")
|
||||
var filePathResult = new TestVirtualFileResult(path, "text/plain")
|
||||
{
|
||||
FileProvider = fileProvider.Object,
|
||||
};
|
||||
|
|
@ -296,18 +299,22 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Assert.Equal(path, ex.FileName);
|
||||
}
|
||||
|
||||
private static IServiceCollection CreateServices()
|
||||
private static IServiceCollection CreateServices(Type executorType)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
var hostingEnvironment = new Mock<IHostingEnvironment>();
|
||||
|
||||
services.AddSingleton(executorType ?? typeof(TestVirtualFileResultExecutor));
|
||||
services.AddSingleton<IHostingEnvironment>(hostingEnvironment.Object);
|
||||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static HttpContext GetHttpContext()
|
||||
private static HttpContext GetHttpContext(Type executorType = null)
|
||||
{
|
||||
var services = CreateServices();
|
||||
var services = CreateServices(executorType);
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = services.BuildServiceProvider();
|
||||
|
|
@ -315,7 +322,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
return httpContext;
|
||||
}
|
||||
|
||||
private IFileProvider GetFileProvider(string path)
|
||||
private static IFileProvider GetFileProvider(string path)
|
||||
{
|
||||
var fileInfo = new Mock<IFileInfo>();
|
||||
fileInfo.SetupGet(fi => fi.Exists).Returns(true);
|
||||
|
|
@ -335,7 +342,24 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
{
|
||||
}
|
||||
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<TestVirtualFileResultExecutor>();
|
||||
executor.IsAscii = IsAscii;
|
||||
return executor.ExecuteAsync(context, this);
|
||||
}
|
||||
|
||||
public bool IsAscii { get; set; } = false;
|
||||
}
|
||||
|
||||
private class TestVirtualFileResultExecutor : VirtualFileResultExecutor
|
||||
{
|
||||
public TestVirtualFileResultExecutor(ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment)
|
||||
: base(loggerFactory,hostingEnvironment)
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsAscii { get; set; }
|
||||
|
||||
protected override Stream GetFileStream(IFileInfo fileInfo)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue