IIS 中的自定义虚拟路径提供程序

问题描述 投票:0回答:4

在 IIS 7.5 中实现自定义虚拟路径提供程序的正确配置是什么? 以下代码在使用 ASP.NET 开发服务器从 Visual Studio 运行时按预期工作,但在从 IIS 运行时不会加载图像。

.NET 4.0 项目文件

CustomVirtualPathProvider.zip - SkyDrive 文件

Web.config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.webServer>
     <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

默认.aspx

<%@ Page Title="Home Page" Language="C#" AutoEventWireup="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Virtual Path Provider</title>
    </head>
    <body>
        <img src="Box.png" />
    </body>
</html>

Global.asax

public class Global : System.Web.HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new WebApplication1.CustomVirtualPathProvider());
    }
}

自定义虚拟文件.cs

public class CustomVirtualFile : System.Web.Hosting.VirtualFile
{
    private string _VirtualPath;

    public CustomVirtualFile(string virtualPath) : base(virtualPath)
    {
        _VirtualPath = virtualPath.Replace("/", string.Empty);
    }

    public override Stream Open()
    {
        string ImageFile =
            System.IO.Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath, @"Crazy\Image\Path", _VirtualPath);
        return System.IO.File.Open(ImageFile, FileMode.Open, FileAccess.Read);
    }
}

自定义VirtualPathProvider.cs

public class CustomVirtualPathProvider : System.Web.Hosting.VirtualPathProvider
{
    Collection<string> ImageTypes;

    public CustomVirtualPathProvider() : base()
    {
        ImageTypes = new Collection<string>();
        ImageTypes.Add(".PNG");
        ImageTypes.Add(".GIF");
    }

    public override bool FileExists(string virtualPath)
    {
        if (IsImage(virtualPath))
        {
            return true;
        }
        return base.FileExists(virtualPath);
    }

    public override System.Web.Hosting.VirtualFile GetFile(string virtualPath)
    {
        if (IsImage(virtualPath))
        {
            return new CustomVirtualFile(virtualPath);
        }
        return base.GetFile(virtualPath);
    }

    private bool IsImage(string file)
    {
        return ImageTypes.IndexOf(file.ToUpperInvariant().Substring(file.Length - 4, 4)) > -1;
    }
}

文件系统

\Crazy\Image\Path\Box.png

IIS 配置

默认站点,没有配置更改。

asp.net iis-7.5
4个回答
6
投票

这是我发现可以“解决”我的问题的方法。

http://sunali.com/2008/01/09/virtualpathprovider-in-precompiled-web-sites/

简而言之:

HostingEnviornment 明确忽略预编译站点中的虚拟路径提供程序。 您可以通过使用反射来调用省略此检查的内部版本来规避此限制。因此,而不是打电话

HostingEnviornment.RegisterVirtualPathProvider(new EmbeddedViewVirtualPathProvider();

请改为:

typeof(HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal",
      BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.NonPublic)
    .Invoke(null, new object[] {new EmbeddedViewPathProvider()});

5
投票

我遇到了同样的问题,但 Tom Clarkson 带领我走上了正确的道路,他完全正确,因为您需要额外的配置才能使 IIS 通过虚拟路径提供程序为内容提供程序提供服务。我找到了解决方案这里

这是一个示例 web.config-snippet,我认为它适合您

<system.web>
  <httpHandlers>
    <add path="*.png" verb="*" type="System.Web.StaticFileHandler" validate="true" />
    <add path="*.gif" verb="*" type="System.Web.StaticFileHandler" validate="true" />
  </httpHandlers>
</system.web>

您还可以在特殊位置(例如“/MyVirtualFiles”)下注册“通配符httphandler”,如果您的虚拟路径提供程序提供多种不同的文件类型,这可能会很有用。

<location path="MyVirtualFiles">
  <system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.StaticFileHandler" validate="true" />
    </httpHandlers>
  </system.web>
</location>

1
投票

当 FileExists 返回 true 时,它被解释为“那里有一个文件,IIS 可以在没有 ASP.NET 的情况下提供它”。为了下一步实际下载文件以通过虚拟路径提供程序,您需要将 IIS 设置为使用 ASP.NET 来提供所有图像文件,并在 global.asax 或 http 处理程序中添加代码,以利用您的虚拟路径提供程序。虚拟路径提供者。


0
投票

我开发了一个“重写”

HostingEnvironment.RegisterVirtualPathProvider
方法的代码。
因此,它绕过了
System.Web.Hosting
库中写入的限制。

您可以使用

VirtualPathHelper.ForceRegisterVirtualPathProvider
设置 VirtualPath。或者您可以在应用程序初始化中调用
VirtualPathHelper.HackSystemHostingEnvironmentRegistration()
方法来破解
HostingEnvironment.RegisterVirtualPathProvider
,因此对您的代码或任何项目库的任何调用都将使用您的注册变体。

/// <summary>
    /// Helper per la gestione dei VirtualPath
    /// </summary>
    public static class VirtualPathHelper
    {
        /// <summary>
        /// Indica se il sito web è precompilato.
        /// </summary>
        public static bool IsPrecompiledApp
        {
            get { return System.Web.Compilation.BuildManager.IsPrecompiledApp; }
        }

        /// <summary>
        /// Forza la registrazione del <paramref name="virtualPathProvider"/>, in quanto funziona anche se
        /// il progetto Web è "precompilato".
        /// Per dettagli sul problema vedere qui: https://sunali.com/2008/01/09/virtualpathprovider-in-precompiled-web-sites/
        /// </summary>
        /// <param name="virtualPathProvider"></param>
        public static void ForceRegisterVirtualPathProvider(VirtualPathProvider virtualPathProvider)
        {
            if (!IsPrecompiledApp)
            {
                // Sito non Precompilato.
                // Non serve alcun Hack.
                System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(virtualPathProvider);
            }
            else
            {
                
                // we get the current instance of HostingEnvironment class. We can't create a new one
                // because it is not allowed to do so. An AppDomain can only have one HostingEnvironment
                // instance.
                var hostingEnvironmentInstance = (System.Web.Hosting.HostingEnvironment)typeof(System.Web.Hosting.HostingEnvironment)
                    .InvokeMember("_theHostingEnvironment", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null);
                if (hostingEnvironmentInstance == null)
                {
                    return;
                }

                // we get the MethodInfo for RegisterVirtualPathProviderInternal method which is internal
                // and also static.
                MethodInfo mi = typeof(System.Web.Hosting.HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal", BindingFlags.NonPublic | BindingFlags.Static);
                if (mi == null)
                {
                    return;
                }

                // finally we invoke RegisterVirtualPathProviderInternal method with one argument which
                // is the instance of our own VirtualPathProvider.
                mi.Invoke(hostingEnvironmentInstance, new object[] { virtualPathProvider });
            }
        }

        /// <summary>
        /// Modifica il puntatore al metodo <see cref="System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider"/> per portare a compimento di tutte le chiamate,
        /// anche in condizione di `<see cref="IsPrecompiledApp"/> = True`.
        /// </summary>
        public static void HackSystemHostingEnvironmentRegistration()
        {
            if (!IsPrecompiledApp)
            {
                // Sito non Precompilato.
                // Non serve alcun Hack.
                return;
            }

            // Ottieni il tipo della classe originale
            var originalMethod = typeof(System.Web.Hosting.HostingEnvironment).GetMethod("RegisterVirtualPathProvider");

            // Ottieni il metodo alternativo (quello che vogliamo eseguire al posto dell'originale)
            var newMethod = typeof(VirtualPathHelper).GetMethod("ForceRegisterVirtualPathProvider");

            // Prepara i metodi per il JIT (necessario per manipolare i puntatori)
            System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(originalMethod.MethodHandle);
            System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(newMethod.MethodHandle);

            // Ottieni i puntatori ai metodi originali
            unsafe
            {
                if (IntPtr.Size == 8) // x64
                {
                    // Ottieni il puntatore alla funzione originale e alla nuova
                    var originalPointer = (long*)originalMethod.MethodHandle.Value.ToPointer() + 1;
                    var newPointer = (long*)newMethod.MethodHandle.Value.ToPointer() + 1;

                    // Sostituisci il puntatore della funzione originale con il puntatore alla nuova funzione
                    *originalPointer = *newPointer;
                }
                else // x86
                {
                    var originalPointer = (int*)originalMethod.MethodHandle.Value.ToPointer() + 1;
                    var newPointer = (int*)newMethod.MethodHandle.Value.ToPointer() + 1;

                    *originalPointer = *newPointer;
                }
            }
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.