【问题标题】:How to get view content from a resource file?如何从资源文件中获取视图内容?
【发布时间】:2019-09-03 21:39:50
【问题描述】:

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

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

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

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

提前致谢。

【问题讨论】:

  • 我看不出 ApiController 和 Controller 之间有什么区别,它们都将模型传递给视图。但是如果你想从文本文件中渲染你的视图,你可以查看github.com/Antaris/RazorEngine

标签: asp.net-mvc razor


【解决方案1】:

我设法让它工作。

对于那些感兴趣的人,请执行以下操作(我假设您从现在开始有一个常规的 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 的常规路径提供者。

  • 最后,在A的Global.asaxApplication_Start方法中注册这个新的路径提供者
HostingEnvironment.RegisterVirtualPathProvider(new ResourceVirtualPathProvider());

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

【讨论】:

    猜你喜欢
    • 2012-12-01
    • 2011-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 2011-05-16
    • 2015-01-12
    • 1970-01-01
    相关资源
    最近更新 更多