如何从资源文件获取视图内容?

时间:2019-09-03 21:39:50

标签: asp.net-mvc razor

在一个ASP.NET MVC项目中,曾有人要求我设计一种将某些控制器及其相应视图嵌入到库项目中的资源文件中的方法。想法是要有一个提供这些控制器及其视图的库,以便不允许开发人员修改或禁用它们。

对于没有视图的控制器(例如ApiController),这是一个简单的任务:将相关的WebApi NuGet包添加到库项目中,向控制器添加带有所需方法的控制器到库项目中,将此库添加为对ASP.NET WebApi项目的依赖关系;控制器将自动出现。但是,当它是常规的MVC Controller时,我需要考虑视图,并且每个渲染器都假定视图的内容在某个文件中。

我要构建的是一种从资源文件而不是磁盘文件加载传统Razor视图的方法。假设我的图书馆项目有一个Views.resx文件,并且内部有一个名为HealthStats的常规文本资源,这是文本文件中常规Razor视图的内容。在同一个库中,有一个Stats控制器,它带有一个HealthStats动作,该动作返回HealthStats视图。这个想法是,如果我将该库添加到ASP.NET项目中作为引用,则Stats控制器可用,并且在调用HealthStats操作时,它将按预期返回视图。

我了解了IViewIViewEngine接口,但是我不确定这是我必须实现的。

谢谢。

1 个答案:

答案 0 :(得分:0)

我设法使其正常工作。

对于那些感兴趣的人,请执行以下操作(假设您从现在开始有一个常规的ASP.NET MVC项目- A ,并且在同一解决方案中有一个单独的类库项目,请致电它 B -):

  • 在B中创建资源文件(在示例中,我使用的名称为 Views.resx )。
  • 将视图作为常规文本文件添加到该资源文件。请注意,您将没有Intellisense。您可以在A中创建视图,然后将其复制/粘贴到B中以便于开发。另外,您应该为在此处创建的资源选择一种命名策略(获取视图内容时将需要它;在本示例中,我使用CONTROLLER_VIEW作为命名模式)。另一个注意事项:您添加到资源文件的视图中的特殊字符将无法正确显示,除非您将它们替换为相应的HTML实体。
  • 在B中实现以下类:
public class ResourceVirtualFile : VirtualFile
{
    private byte[] _content;

    public byte[] Content
    {
        get { return _content; }
    }

    public ResourceVirtualFile(string virtualPath, byte[] content)
        : base(virtualPath)
    {
        _content = content;
    }

    public override Stream Open()
    {
        return new MemoryStream(_content);
    }
}
public class ResourceVirtualPathProvider : VirtualPathProvider
{
    private static Dictionary<string, string> _resource_entries;
    private static Regex _virtual_path_regex;

    static ResourceVirtualPathProvider()
    {
        _resource_entries = Views
            .ResourceManager
            .GetResourceSet(CultureInfo.InvariantCulture, true, true)
            .OfType<DictionaryEntry>()
            .ToDictionary(d => d.Key as string, d => d.Value as string);
        _virtual_path_regex = new Regex(@"~{0,1}\/Views\/(?'controller'[\w]+)\/(?'view'[\w]+)\.cshtml$");
    }

    public override bool FileExists(string virtualPath)
    {
        var view_exists = CheckViewExistsInResource(virtualPath);
        if (view_exists)
        {
            return true;
        }
        return base.FileExists(virtualPath);
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        var view_content = GetViewFromResource(virtualPath);
        if (view_content == null)
        {
            return base.GetFile(virtualPath);
        }
        else
        {
            return new ResourceVirtualFile(virtualPath, view_content);
        }
    }

    public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        var view_content = GetViewFromResource(virtualPath);
        if (view_content != null)
        {
            return null;
        }
        else
        {
            return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        }
    }

    private string ProcessVirtualPath(string virtualPath)
    {
        var match = _virtual_path_regex.Match(virtualPath);
        if (!match.Success)
        {
            return null;
        }
        var resource_name = string.Format(
            "{0}_{1}",
            match.Groups["controller"].Value,
            match.Groups["view"].Value);
        return resource_name;
    }

    private bool CheckViewExistsInResource(string virtualPath)
    {
        var resource_name = ProcessVirtualPath(virtualPath);
        if (string.IsNullOrEmpty(resource_name))
        {
            return false;
        }
        var contains_view = _resource_entries.ContainsKey(resource_name);
        return contains_view;
    }

    private byte[] GetViewFromResource(string virtualPath)
    {
        var resource_name = ProcessVirtualPath(virtualPath);
        if (string.IsNullOrEmpty(resource_name))
        {
            return null;
        }
        var contains_view = _resource_entries.ContainsKey(resource_name);
        if (!contains_view)
        {
            return null;
        }
        var view_content = Views.ResourceManager.GetString(resource_name);
        var view_bytes = Encoding.UTF8.GetBytes(view_content);
        return view_bytes;
    }
}

这些类允许您实现路径提供程序,该路径提供程序将MVC的路径请求转换为可以从B的资源文件中获取的名称。如果ResourceVirtualPathProvider无法为您获取视图,它将服从A的常规路径提供程序。

  • 最后,使用Global.asax方法在A的Application_Start中注册此新路径提供程序
HostingEnvironment.RegisterVirtualPathProvider(new ResourceVirtualPathProvider());

您已完成。在B中创建任意数量的视图;只要您尊重命名模式,就可以找到并显示视图。