1

The question "https://stackoverflow.com/questions/16397785/add-pages-to-mvc-deployed-website" seems to relate closely to the question I want to ask. But it my case, I have not yet developed an application, but I want to develop it in such a way that it can be customized by users after it is deployed by adding files to the deployed directory. What would be the advisable way of doing this? Specifically, I want users to be able to define custom pages, possibly replace existing pages or add controls to existing pages, and possibly define custom WebAPI functions to retrieve custom data. I tried adding .vbhtml files to the Views directory as described, but ran into the same problem described in the linked question.

4 Answers 4

0

Razor views are compiled at runtime, so you always deploy them un-compiled.

What you want is simply Razor pages, so can add them to any directory (not Views) and access them without the file extension (e.g. /Foo/Bar instead of /Foo/Bar.vbhtml).

Sign up to request clarification or add additional context in comments.

17 Comments

But in order to access a view, you apparently need to have a controller compiled in front of it. Or am I missing something?
Razor pages don't need controllers, just like .aspx. See asp.net/web-pages
In order for a Razor View to be rendered you need a Controller/Action to answer for the inboud HTTP request. You can build a modified version of the Razor view engine that would be capable of looking for customizated controller action in a plugin like architecture but that is a lot of work to build but can be done.
@NickBork That's why I said Razor page and not Razor view.
When I try to access a razor page outside of the Views directory, I get "The type of page you have requested is not served because it has been explicitly forbidden. The extension '.vbhtml' may be incorrect. Please review the URL below and make sure that it is spelled correctly."
|
0

I don't think I would recommend using the filesystem for this. I think you should save the razor code to the database and then, when you wish to parse it, you may do so according to this Using RazorEngine to parse Razor templates concurrently

Here is another example:

var model = new { Name = "Test" };
var template = "Hello @Model.Name";

var result = Razor.Parse(template, model);

Code taken from Using Razor engine on strings - not views

Edit to answer your questions:

The razor code would be stored in the database along with whatever controller you wanted to run it. When you retrieve the razor code from the database, you would also know the controller, then you could redirect to that controller, sending whatever model you want, to the razor code, and then parse it as shown above. Make sense?

7 Comments

Does this restrict what can be customized? For example, does it only allow custom views to be defined and not custom controllers or models? Does it only allow existing views to be customized and not allow the creation of new views? Not being clear on how controller, model and view integrate in this scheme, I'm confused about where this code would go. In a controller? In a view?
There isn't enough information to suggest that you should or should not use the file system to store the views. One could easily use a VirtualPathProvider and store the views in the database instead of the file system. The real question is in what manner does BlueMonkMN want the users to be able to edit them after the are deployed. Assuming that each installation is a single user's site the user would have access to the file system and could modify views. Keep in mind that changing the views does not change how the application behaves as that is all contained within the controller actions.
See my edit. Not using the filesystem is just a personal preference based on my experience.
I'm still confused. With controllers being the entry point to the code, how would ASP.NET MVC know to load a controller from the database when redirecting to it instead of loading it from the dll. Or is the set of controllers pre-determined and hosted on in compiled code as always (un-customizable)?
See stackoverflow.com/questions/7892094/… which will show you how you can redirect from one controller to another. On each controller, you could implement a "Render" method which would send the model to the parsed razor view (after retrieving it from the database).
|
0

The routing configuration can be customized to identify a URL pattern that identifies all requests for customized code and route them through a common controller that can then load arbitrary views based on information provided in the URL. Notice the route with the name Custom below, and how it always uses the Index action of a controller named Custom, but introduces a new parameter customization.

Public Class RouteConfig
   Public Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

      routes.MapRoute( _
         name:="Custom", _
         url:="custom/{customization}/{id}", _
         defaults:=New With {.controller = "Custom", .action = "Index", .id = UrlParameter.Optional} _
      )

      routes.MapRoute( _
            name:="Default", _
            url:="{controller}/{action}/{id}", _
            defaults:=New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional} _
        )
   End Sub
End Class

Once this is set up, add a controller called CustomController to the project that simply passes through to a view determined by the customization parameter:

Public Class CustomController
   Inherits System.Web.Mvc.Controller

   '
   ' GET: /Custom

   Function Index(customization As String) As ActionResult
      Return View(customization)
   End Function

End Class

Publish this code and add a Custom directory under the Views directory in the deployed location. Now you can put any .vbhtml file in the Custom folder and refer to it with a URL like http://localhost/MyApplication/Custom/MyView, which will load MyView.vbhtml from the Views\Custom directory.

2 Comments

The only thing this solution does is allow you to pass the name of a View to a pre-defined controller action and force it to return that view. You're lacking the ability for users to customize the behavior of a controller action and also the model that gets passed to the view. If ALL you want is custom views why not implement a VirtualPathProvider, store the customized views in the database and there would be no need for this "Custom" controller?
It also allows me to add code into the Index function to call out to custom code based on which customization is being executed. That effectively allows me to define custom controllers too, once I figure out what a controller needs to do besides select a view (which may be nothing). In this way I can funnel all customizations through one place, but allow an arbitrary number of arbitrarily named customizations. This custom controller seems simpler than implementing a VirtualPathProvider, and also provides a means to call out to custom controller code based on which customization is executing.
0

If the main intent is to provide extensibility, then there is a simpler answer. ASP.Net will probe all assemblies in the bin directory of the deployed web application and pick up all controllers and models in compiled assemblies there, as well as all un-compiled views in the Views directory. With some effort I was able to determine a minimal set of files necessary to create an independent template project that could be used by people who would develop and deploy custom code into the running (deployed) web application. The details are provided as an answer to a more relevant question I discovered on this topic because it was not straightforward to get Intellisense and other ASP.Net MVC4 apsects of this template project working. See https://stackoverflow.com/a/21122377/78162

Since Visual Studio Express is now available for free, my hope is that such a template project could be loaded as a starting point for developing customizations to another ASP.Net MVC4 application. My limited testing indicates this will work for both the UI layer (as demonstrated in previous link) and the API layers (as discussed at https://stackoverflow.com/a/21028825/78162).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.