我找到了适合我们的解决方案。我们尚未完全实施这一点,因此可能仍会出现一些无法预料的问题。
首先,创建一个 MVC 项目,其中包含满足您的包要求所需的视图、控制器、javascript 等。每个静态文件和视图都必须在项目中设置为嵌入式资源。
然后,添加一个类以在虚拟路径提供程序上提供这些文件。这将允许消费项目访问静态文件和视图,就好像它们在同一个项目中一样。
要启用自定义路由,需要实现 RouteBase 类。此实现需要接受 string 属性,虚拟路由所基于的属性允许主机应用所需的任何路由前缀。对于我们的示例,该属性将默认为 Booking,并与我们项目中的相关视图架构相匹配。
RouteBase 实现和VirtualPath 类都将在设置方法中实例化。这将允许消费项目调用单个方法来设置预订引擎。此方法将采用站点路由集合和动态路由属性来附加自定义路由。该方法还将VirtualPathProvider注册到HostingEnvironment对象。
消费主机也可以覆盖视图和任何其他静态文件,只需在主机项目中与预订引擎中的文件或视图路径匹配的位置放置一个文件即可。
一些代码示例
RouteBase 方法,如果传入路由与虚拟路由匹配,则返回正确的路由值。
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
// Trim the leading slash
var path = httpContext.Request.Path.Substring(1);
// Get the page that matches.
var page = GetPageList(httpContext)
.Where(x => x.VirtualPath.Equals(path))
.FirstOrDefault();
if (page != null)
{
result = new RouteData(this, new MvcRouteHandler());
// Optional - make query string values into route values.
AddQueryStringParametersToRouteData(result, httpContext);
result.Values["controller"] = page.Controller;
result.Values["action"] = page.Action;
}
// IMPORTANT: Always return null if there is no match.
// This tells .NET routing to check the next route that is registered.
return result;
}
RouteBase 虚拟路由到 NuGet 包路由映射。使用动态虚拟路径字符串和对真实控制器和操作名称的引用创建的新 PageInfo 对象。然后将它们存储在 http 上下文缓存中。
private IEnumerable<PageInfo> GetPageList(HttpContextBase httpContext)
{
string key = "__CustomPageList";
var pages = httpContext.Cache[key];
if (pages == null)
{
lock (synclock)
{
pages = httpContext.Cache[key];
if (pages == null)
{
pages = new List<PageInfo>()
{
new PageInfo()
{
VirtualPath = string.Format("{0}/Contact", BookingEngine.Route),
Controller = "Home",
Action = "Contact"
},
};
httpContext.Cache.Insert(
key: key,
value: pages,
dependencies: null,
absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration: TimeSpan.FromMinutes(1),
priority: System.Web.Caching.CacheItemPriority.NotRemovable,
onRemoveCallback: null);
}
}
}
return (IEnumerable<PageInfo>)pages;
}
Booking Engine 类设置方法,它执行程序集所需的所有实例化。
public class BookingEngine
{
public static string Route = "Booking";
public static void Setup(RouteCollection routes, string route)
{
Route = route;
HostingEnvironment.RegisterVirtualPathProvider(
new EmbeddedVirtualPathProvider());
routes.Add(
name: "CustomPage",
item: new CustomRouteController());
}
}
嵌入式虚拟文件
public override CacheDependency GetCacheDependency(string virtualPath, virtualPathDependencies, DateTime utcStart)
{
string embedded = _GetEmbeddedPath(virtualPath);
// not embedded? fall back
if (string.IsNullOrEmpty(embedded))
return base.GetCacheDependency(virtualPath,
virtualPathDependencies, utcStart);
// there is no cache dependency for embedded resources
return null;
}
public override bool FileExists(string virtualPath)
{
string embedded = _GetEmbeddedPath(virtualPath);
// You can override the embed by placing a real file at the virtual path...
return base.FileExists(virtualPath) || !string.IsNullOrEmpty(embedded);
}
public override VirtualFile GetFile(string virtualPath)
{
// You can override the embed by placing a real file at the virtual path...
if (base.FileExists(virtualPath))
return base.GetFile(virtualPath);
string embedded = _GetEmbeddedPath(virtualPath);
if (string.IsNullOrEmpty(embedded))
return null;
return new EmbeddedVirtualFile(virtualPath, GetType().Assembly
.GetManifestResourceStream(embedded));
}
private string _GetEmbeddedPath(string path)
{
if (path.StartsWith("~/"))
path = path.Substring(1);
path = path.Replace(BookingEngine.Route, "/");
//path = path.ToLowerInvariant();
path = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + path.Replace('/', '.');
// this makes sure the "virtual path" exists as an embedded resource
return GetType().Assembly.GetManifestResourceNames()
.Where(o => o == path).FirstOrDefault();
}
嵌套虚拟文件类
public class EmbeddedVirtualFile : VirtualFile
{
private Stream _stream;
public EmbeddedVirtualFile(string virtualPath,
Stream stream) : base(virtualPath)
{
if (null == stream)
throw new ArgumentNullException("stream");
_stream = stream;
}
public override Stream Open()
{
return _stream;
}
}
我们使用的很多代码来自以下链接
嵌入式文件 - https://www.ianmariano.com/2013/06/11/embedded-razor-views-in-mvc-4/
RouteBase 实现 - Multiple levels in MVC custom routing