Lately my focus has been on creating an ASP.NET MVC application that can host 3rd party MVC plugins. Ideally, development of those plugins would follow these rules:
- Plugins can be developed in a standard MVC project, and be able to use all the existing infrastructure of the ASP.NET MVC framework.
- The complexity of compiling the plugin MVC project and including into a host MVC application should not be severe.
- Any changes to the normal development flow of an MVC application would be the bear minimum
After some research I've come up with the following approaches to achieve this, each with their own advantages and disadvantages.
Approach 1 - MVC plugin assembly loaded into main MVC AppDomain
Work Flow
- Develop plugin inside a separate MVC project.
- Compile the assembly and load it and any dependencies into the host application via
PreApplicationStartMethodAttribute
, MEF or a basic assembly reference within the host project (if possible). - Map a route to the plugin controllers so that the plugin is treated as an
Area
within the host. - Place the plugin views into the correct area folder. The layout file will need to be changed so that the layout path points to an area based location, rather than the root of the application (which would have been the case in the development MVC project)
- When a request comes in for the plugin, ASP.NET will use the existing areas functionality to route the request to the correct controllers and look in the correct location for the view files.
Advantages
- Will work as seamlessly as if the controllers were embedded in the host MVC application assembly.
- Simple to include the assemblies into the host App Domain before application start (
PreApplicationStartMethodAttribute
, project reference) and after application start (MEF)
Disadvantages
- No sandboxing - the controllers will have the same trust level as host.
Conclusion
This is the easiest approach, however it is also the least secure. It essentially removes the possibility of allowing untrusted developers to create plugins because those plugins will have the same trust level as the host application (meaning that if the host application can execute methods such as System.IO.File.Delete
, so can the plugin)
Approach 2 - MVC plugin assembly running in own AppDomain via MAF
This approach intends to allow the creation of MVC plugins that can be sandboxed into their own AppDomains
and used by the host via the System.Addin
libraries.
Structure
A route is setup in the host that determines whether the url being processed is targeting a plugin. May have a pattern such as
example.com/p/{plugin}/{controller}/{action}/{id}
All routes that have the above pattern are mapped to a host controller that has a module routing action. That action looks at any given route and determines the appropriate plugin to process the request based on the
{plugin}
segment.The plugin view is a receiver/sender object that acts as a gateway to the plugin controllers. It has a method called AcceptRequest that receives a
RequestContext
from the host, and that returns anActionResult
.The plugin pipeline contains adapters that can serialise
RequestContext
andActionResult
for transmission across the isolation boundary of the pipeline.
Execution Flow
A route for a plugin is matched and the plugin routing controller is called.
The controller loads the required plugin into it's own
AppDomain
and calls AcceptRequest, passing through theRequestContext
(which is serialised via the pipeline)AcceptRequest receives the context and determines the appropriate controller to execute based on that request (using a custom controller factory).
Once the controller has finished executing the request, it returns an
ActionResult
to the receiver object which then passed thatActionResult
(also serialised via the pipeline) back to the hostAppDomain
.The controller that initially called AcceptRequest can then return the
ActionResult
to the host MVC execution pipeline, as if it processed the request itself. The pluginAppDomain
can then be unloaded if so wished.
Advantages
Plugin will be sandboxed in it's AppDomain
, thus any permission set can be used that suits the host.
Disadvantages
- Would have to be possible to serialise
RequestContext
andActionResult
. - Will possibly break other ASP.NET MVC functionality within the isolated
AppDomain
.
Conclusion
This approach sounds good on paper, but I'm not sure if it's possible/feasible to serialise the RequestContext
and ActionResult
objects, as well as run an MVC controller in isolation.
Questions
The first approach is fine if the code is being created by trusted developers. I know that I'm not going delete all the host view files or it's web.config file. But ultimately, if you want third party devs to create plugins for your MVC app, you need to be able to sandbox their code.
From all my research, the System.Addin
library makes it easy to implement a host/plugin environment when you are using simple API based class libraries. However it seems that it isn't easy to do this when MVC is involved.
Some questions I have are:
- Is the second approach I've outlined here feasible?
- Is there a better way to do this?
- Is there going to be easier ways to achieve MVC plugin isolation in the future?