Hosting ASP.Net Mvc in SharePoint
In Office SharePoint Server 2007 & Windows SharePoint Services 3
1. Enabling ASP.Net 3.5
- Start Visual Studio 2008.
- Create a new dummy ASP.NET Web Application Project, and make sure you target the .NET Framework 2.0 (upper right dropdown of the New Project dialog). The name of this project is not important; you won’t need it anymore when we’re done.
- Copy the web.config of your SharePoint 2007 site, into the dummy Web Application project in Visual Studio.
- Open the Project Properties in Visual Studio (right click on the Project node in the Solution Explorer, and choose Properties; or in the Project menu, select WebApplicationX Properties).
- Select .NET Framework 3.5 in the Target Framework dropdown (select Yes in the confirmation dialog).
- Copy the web.config from the Web Application Project back to SharePoint.
- If you want to use <ScriptManager> in SharePoint MasterPage,you need to modify the web.config,add node in <SafeControls>.
<SafeControls>
<SafeControl Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TypeName="*" Safe="True" />
</SafeControls>
- Register Routes and ASP.Net Mvc
2. URL Routing
- Modify the web.config of your sharepoint site,add nodes like bellow:
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpHandlers>
<httpModules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>
<compilation batch="false" debug="false">
<assemblies>
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</assemblies>
</compilation>
<pages enableSessionState="false" enableViewState="true" enableViewStateMac="true" validateRequest="false"
pageParserFilterType="Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
asyncTimeout="7">
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/>
</namespaces>
</pages>
</system.web>
<system.webServer>
<handlers>
<remove name="MvcHttpHandler"/>
<remove name="UrlRoutingHandler"/>
<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc"
type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd"
type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</handlers>
</system.webServer>
</configuration>
Ok,if you see a error page, then you can
see real error description and callstack/stack trace like below:
[ArgumentException: virtualPath]
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.IsExcludedPath(String virtualPath) +486
Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider.FileExists(String virtualPath) +188
System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext) +148
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +34
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
Then,Reflecting the System.Web.Routing.
RouteCollection Constructor
public RouteCollection() : this(HostingEnvironment.VirtualPathProvider)
{
}
public RouteCollection(VirtualPathProvider virtualPathProvider)
{
this._namedMap = new Dictionary<string, RouteBase>(StringComparer.OrdinalIgnoreCase);
this._rwLock = new ReaderWriterLock();
this._vpp = virtualPathProvider;
}
RouteCollection.GetRouteData Method
public RouteData GetRouteData(HttpContextBase httpContext)
{
// other codes...
if (!this.RouteExistingFiles)
{
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (((appRelativeCurrentExecutionFilePath != "~/")
&& (this._vpp != null))
&& (this._vpp.FileExists(appRelativeCurrentExecutionFilePath)
|| this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
{
return null;
}
}
// other codes...
}
After Reflecting the issue, as we know
HttpRequest.AppRelativeCurrentExecutionFilePath Property maybe start with "~".But it turns out that WSS own
VirtualPathProvider (SPVirtualPathProvider,
briefly described by this MSDN page) refuses to handle requests for which the virtual path starts with "~".
So where cause this.
- Custom MvcVirtualPathProvider
/// <summary>
/// Filtrate the virtual path when that starts with '~' for SPVirtualPathProvider.
/// </summary>
public class MvcVirtualPathProvider : VirtualPathProvider
{
public override string CombineVirtualPaths(string basePath, string relativePath)
{
return Previous.CombineVirtualPaths(basePath, relativePath); ;
}
public override bool DirectoryExists(string virtualDir)
{
if (virtualDir.StartsWith("~/"))
{
virtualDir = virtualDir.Remove(0, 1);
}
return Previous.DirectoryExists(virtualDir);
}
public override bool FileExists(string virtualPath)
{
if (virtualPath.StartsWith("~/"))
{
virtualPath = virtualPath.Remove(0, 1);
}
return Previous.FileExists(virtualPath);
}
}
You can get more info at
SharePoint 2007 as a WCF host - Step #4, Write a Virtual Path Provider,but don't register the MvcVirtualPathProvider in HttpModule.I'll explane why bellow.
- Register VirtualPathProvider and Routes
In ASP.Net Mvc,if you use custom VirtualPathProvider to retrieve resources from a virtual file system in you site,you must register the custom VirtualPathProvider in the global.asax.You can get more info form
ASP.NET MVC Plugins.
So:
/// <summary>
/// Register SPVirtualPathProvider,MvcVirtualPathProvider and Routes
/// </summary>
public class MvcHttpApplication : SPHttpApplication
{
public static void RegisterVirtualPathProviders()
{
//Reflection the SPVirtualPathProvider
Assembly assembly = Assembly.Load("Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
Type type = assembly.GetType("Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider");
Object obj = assembly.CreateInstance("Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider");
HostingEnvironment.RegisterVirtualPathProvider((VirtualPathProvider)obj);
HostingEnvironment.RegisterVirtualPathProvider(new MvcVirtualPathProvider());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterVirtualPathProviders();
RegisterRoutes(RouteTable.Routes);
//RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
//RouteTable.Routes.RouteExistingFiles = false;
}
}
Now put both of the above in the GAC (I put them both as 2 classes in a single assembly called SimOne.SharePoint.Solutions). You can download the
source code
<%@ Assembly Name="Microsoft.SharePoint" %>
<%@ Assembly Name="SimOne.SharePoint.Solutions" %>
<%@ Import Namespace="SimOne.SharePoint.ApplicationRuntime" %>
<%@ Application Language="C#" Inherits="SimOne.SharePoint.ApplicationRuntime.MvcHttpApplication" %>
Now,refresh your sharepoint site index page (like:http://yourdomain/default.aspx) and others pages,if it looks fine,

Congratulations! It hit half.
3. Views,MasterPage,ViewEngine
Put your ASP.Net Mvc Project's Content,Scripts,Views folders to the root of your sharepoint site by SharePoint Designer, and I'm Sorry for the designer won't work,and you should do this at first. :-P.
If you want to cancle routing.
- In global.asax,return to <%@ Application Language="C#" Inherits="Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication" %>
- In web.config,return to <!--<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>-->
Now,you can use SharePoint Designer as usual.
Modify the web.config,add the nodes like bellow:
<configuration>
<SharePoint>
<SafeMode MaxControls="200" CallStack="true" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false">
<PageParserPaths>
<PageParserPath VirtualPath="/Views/*" CompilationMode="Always" AllowServerSideScript="true" IncludeSubFolders="true"/>
</PageParserPaths>
</SafeMode>
<SafeControls>
<SafeControl Assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.Mvc" TypeName="ViewMasterPage" Safe="True" />
<SafeControl Assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.Mvc" TypeName="ViewPage" Safe="True" />
<SafeControl Assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.Mvc" TypeName="ViewUserControl" Safe="True" />
</SafeControls>
</SharePoint>
</configuration>
Visit your sharepoint site again,you will find your ASP.Net Mvc project look fine in SharePoint.
In Office SharePoint Server 2010 & SharePoint Foundation 2010 (Windows SharePoint Services 2010)
TO BE CONTINUE.
I'll talk about the masterpage and ViewEngine later,Then you can see the ASP.Net Mvc project can use the sharepoint default masterpage.


Sorry for my poor english. I'll record a video for this.Wish this essay will help you.if you have any idea about how to host ASP.Net Mvc in SharePoint,you can leave your word.