最近我的重点是创建一个可以托管 3rd 方 MVC 插件的 ASP.NET MVC 应用程序。理想情况下,这些插件的开发将遵循以下规则:
- 插件可以在标准的 MVC 项目中开发,并且能够使用 ASP.NET MVC 框架的所有现有基础设施。
- 编译插件 MVC 项目并将其包含到主机 MVC 应用程序中的复杂性不应该很严重。
- 对 MVC 应用程序的正常开发流程的任何更改都是最低限度的
经过一些研究,我想出了以下方法来实现这一点,每种方法都有自己的优点和缺点。
方法 1 - MVC 插件程序集加载到主 MVC AppDomain
工作流程
- 在单独的 MVC 项目中开发插件。
- 编译程序集并通过MEF或宿主项目中的基本程序集引用(如果可能)将它和任何依赖项加载到宿主应用程序
PreApplicationStartMethodAttribute
中。 - 将路由映射到插件控制器,以便将插件视为
Area
主机内的一个。 - 将插件视图放入正确的区域文件夹中。需要更改布局文件,以便布局路径指向基于区域的位置,而不是应用程序的根目录(开发 MVC 项目中就是这种情况)
- 当插件请求进入时,ASP.NET 将使用现有区域功能将请求路由到正确的控制器并在正确的位置查找视图文件。
优点
- 将像控制器嵌入到主机 MVC 应用程序程序集中一样无缝地工作。
PreApplicationStartMethodAttribute
在应用程序启动之前(项目参考)和应用程序启动之后(MEF)简单地将程序集包含到主机应用程序域中
缺点
- 没有沙盒 - 控制器将具有与主机相同的信任级别。
结论
这是最简单的方法,但也是最不安全的。它基本上消除了允许不受信任的开发人员创建插件的可能性,因为这些插件将具有与宿主应用程序相同的信任级别(这意味着如果宿主应用程序可以执行诸如 之类的方法System.IO.File.Delete
,那么插件也可以)
方法 2 - 通过 MAF 在自己的 AppDomain 中运行的 MVC 插件程序集
这种方法旨在允许创建 MVC 插件,这些插件可以被沙箱化AppDomains
并由主机通过System.Addin
库使用。
结构
在主机中设置了一个路由,用于确定正在处理的 url 是否针对插件。可能有一个模式,如
example.com/p/{plugin}/{controller}/{action}/{id}
具有上述模式的所有路由都映射到具有模块路由操作的主机控制器。该操作查看任何给定的路由,并根据
{plugin}
段确定适当的插件来处理请求。插件视图是一个接收器/发送器对象,充当插件控制器的网关。它有一个名为AcceptRequest的方法,它接收
RequestContext
来自主机的 a,并返回一个ActionResult
.插件管道包含可以串行化
RequestContext
和ActionResult
跨管道隔离边界传输的适配器。
执行流程
匹配插件的路由并调用插件路由控制器。
控制器将所需的插件加载到自己的插件中
AppDomain
并调用AcceptRequest,通过RequestContext
(通过管道序列化)AcceptRequest接收上下文并根据该请求确定要执行的适当控制器(使用自定义控制器工厂)。
一旦控制器完成了请求的执行,它
ActionResult
会向接收者对象返回一个,然后将它ActionResult
(也通过管道序列化)传递回主机AppDomain
。最初调用AcceptRequest的控制器然后可以将其返回
ActionResult
给主机 MVC 执行管道,就好像它自己处理了请求一样。AppDomain
如果愿意,可以卸载插件。
优点
插件将被沙箱AppDomain
化,因此可以使用适合主机的任何权限集。
缺点
- 必须可以序列化
RequestContext
和ActionResult
. - 可能会破坏隔离的
AppDomain
.
结论
这种方法在纸上听起来不错,但我不确定序列化RequestContext
andActionResult
对象是否可能/可行,以及单独运行 MVC 控制器。
问题
如果代码是由受信任的开发人员创建的,则第一种方法很好。我知道我不会删除所有主机视图文件或它的 web.config 文件。但最终,如果您希望第三方开发人员为您的 MVC 应用程序创建插件,您需要能够对他们的代码进行沙箱处理。
根据我的所有研究,System.Addin
当您使用基于 API 的简单类库时,该库可以轻松实现主机/插件环境。然而,当涉及到 MVC 时,似乎并不容易做到这一点。
我的一些问题是:
- 我在这里概述的第二种方法可行吗?
- 有一个更好的方法吗?
- 未来是否会有更简单的方法来实现 MVC 插件隔离?