在一个ASP.NET MVC项目中,曾有人要求我设计一种将某些控制器及其相应视图嵌入到库项目中的资源文件中的方法。想法是要有一个提供这些控制器及其视图的库,以便不允许开发人员修改或禁用它们。
对于没有视图的控制器(例如ApiController
),这是一个简单的任务:将相关的WebApi NuGet包添加到库项目中,向控制器添加带有所需方法的控制器到库项目中,将此库添加为对ASP.NET WebApi项目的依赖关系;控制器将自动出现。但是,当它是常规的MVC Controller
时,我需要考虑视图,并且每个渲染器都假定视图的内容在某个文件中。
我要构建的是一种从资源文件而不是磁盘文件加载传统Razor视图的方法。假设我的图书馆项目有一个Views.resx
文件,并且内部有一个名为HealthStats
的常规文本资源,这是文本文件中常规Razor视图的内容。在同一个库中,有一个Stats
控制器,它带有一个HealthStats
动作,该动作返回HealthStats
视图。这个想法是,如果我将该库添加到ASP.NET项目中作为引用,则Stats
控制器可用,并且在调用HealthStats
操作时,它将按预期返回视图。
我了解了IView
和IViewEngine
接口,但是我不确定这是我必须实现的。
谢谢。
答案 0 :(得分:0)
我设法使其正常工作。
对于那些感兴趣的人,请执行以下操作(假设您从现在开始有一个常规的ASP.NET MVC项目- A ,并且在同一解决方案中有一个单独的类库项目,请致电它 B -):
CONTROLLER_VIEW
作为命名模式)。另一个注意事项:您添加到资源文件的视图中的特殊字符将无法正确显示,除非您将它们替换为相应的HTML实体。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中创建任意数量的视图;只要您尊重命名模式,就可以找到并显示视图。