Posts tagged with: asp.net-mvc3

Updated the localization administration area

I’ve updated the administration area for the localization features in an attempt to make it easier to handle the localization.

I’ve replaced the link texts with icons so that they take less space. I’ve also added filtering options (so that you can translate one area/controller at a time) and a filtering option which allows you to only show texts that hasn’t been translated yet.

Update the nuget package (“griffin.mvccontrib.admin”) to get it.


ASP.NET MVC3: Custom error for 401 Unauthorized

Have you tried to display a custom error message if authorization fails..

[Authorize(Roles="User")]
public class ItemController : Controller
{
}

.. by specifying it in the customErrors section in web.config:

    <customErrors defaultRedirect="~/Error/" mode="On">
      <error statusCode="404" redirect="~/Error/NotFound/" />
      <error redirect="~/Error/NotAuthorized/" statusCode="401" />
    </customErrors>

So did I without getting it to work. The [HandleError] attribute can not handle it either since it only handles exceptions (as the contract IExceptionFilter says).

Solution

public class BetterAuthorizeAttribute : AuthorizeAttribute
{
	protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
	{
		throw new NotAuthorizedHttpException(Roles);
	}
}

public class NotAuthorizedHttpException : HttpException
{
	public NotAuthorizedHttpException(string missingRoles)
		: base(401, string.Format("You do not have the required role(s) '{0}'.", string.Join(", ", missingRoles)))
	{
	}
}

That solution allows us to use the exception filter (HandleErrorAttribute) to serve the error page. Customization get’s easier since we only have to make a change in global.asax to be able to serve any page. (i.e. switch the HandleError filter to our custom one).


How to handle transactions in ASP.NET MVC3

I got annoyed about having to repeat the transaction handling in every POST method of my controllers. First of all: I don’t want to have to take the unit of work in the constructor of each controller, since that implicitly says that all/most methods uses transactions. And I do not want to have to resolve a factory, since that says that all methods might use transactions. Finally using a singleton is the worst of solutions.

Before

The problem wasn’t that hard to solve thanks to the flexibility of ASP.NET MVC3. But let’s start by looking at a typical post method:

[HttpPost]
public virtual ActionResult Create(CreateModel model)
{
	if (!ModelState.IsValid)
		return View(model);

	var instruction = new Instruction(CurrentUser);
	Mapper.Map(model, instruction);

	using (var uow = _unitOfWorkFactory.Create())
	{
		_repository.Save(instruction);
		
		uow.SaveChanges();
	}
	
	return RedirectToAction("Details", new {id = instruction.Id});
}

After

What I did was to create a new action filter called TransactionalAttribute. It checks if the model state is valid and that no exceptions have been thrown. It uses the DependencyResolver to find a IUnitOfWork implementation if everything checks out OK. Since this is done in an action filter, the transaction will not be created unless it’s actually required. All you have to do is to register the UoW factory in your IoC container and tag your action:

[HttpPost, Transactional]
public virtual ActionResult Create(CreateModel model)
{
	if (!ModelState.IsValid)
		return View(model);

	var instruction = new Instruction(CurrentUser);
	Mapper.Map(model, instruction);
	_repository.Save(instruction);
	
	return RedirectToAction("Details", new {id = instruction.Id});
}

There is a small catch: You must add an error to the ModelState if you catch exceptions in your class. The transaction will otherwise get committed.

[HttpPost, Transactional]
public virtual ActionResult Create(CreateModel model)
{
	if (!ModelState.IsValid)
		return View(model);

	try
	{
		model.Category = model.Category ?? "Allmänt";

		var instruction = new Instruction(CurrentUser);
		Mapper.Map(model, instruction);
		_repository.Save(instruction);
		
		return RedirectToAction("Details", new {id = instruction.Id});
	}
	catch (Exception err)
	{
		// Adds an error to prevent commit.
		ModelState.AddModelError("", err.Message);
		Logger.Error("Failed to save instruction for app " + CurrentApplication, err);
		return View(model);
	}
}

Implementation

The attribute itself looks like this:

public class TransactionalAttribute : ActionFilterAttribute
{
	private IUnitOfWork _unitOfWork;

	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		if (filterContext.Controller.ViewData.ModelState.IsValid && filterContext.HttpContext.Error == null)
			_unitOfWork = DependencyResolver.Current.GetService<IUnitOfWork>();

		base.OnActionExecuting(filterContext);
	}

	public override void OnActionExecuted(ActionExecutedContext filterContext)
	{
		if (filterContext.Controller.ViewData.ModelState.IsValid && filterContext.HttpContext.Error == null && _unitOfWork != null)
			_unitOfWork.SaveChanges();

		base.OnActionExecuted(filterContext);
	}
}

Simple, but effective! :)

Extras

The actual unit of work implementation depends on which kind of data layer you are using. You can use the following code snippet if you are using nhibernate and have successfully registered the ISession in your container:

public class NhibernateUnitOfWork : IUnitOfWork
{
	private readonly ISession _session;
	private ITransaction _transaction;

	public NhibernateUnitOfWork(ISession session)
	{
		_session = session;
		_transaction = session.BeginTransaction();
	}

	public void Dispose()
	{
		if (_transaction == null)
			return;

		if (!_transaction.WasCommitted)
			_transaction.Rollback();

		_transaction.Dispose();
		_transaction = null;
	}

	public void SaveChanges()
	{
		_transaction.Commit();
	}

}

The Unit of work interface is really simple:

public interface IUnitOfWork : IDisposable
{
	void SaveChanges();
}

Griffin.MvcContrib – The plugin system

Introduction

Griffin.MvcContrib is a contribution project for ASP.NET MVC3 which contains different features like extendable HTML Helpers (you can modify the HTML that the existing helpers generate), easy localization support and a quite fresh support for plugin development. The purpose of this article is to provide a step-by-step instruction on how you can develop plugins using Griffin.MvcContrib.

Using the code

The plugin support are based on the Area support which MVC3 got. If you haven’t used areas before, I suggest that you start by reading the following article.

Most basic approach.

This first sample is not really a plugin system but only showing how you can move your code to class libraries. The example will only contain one class library to avoid confusion.
Start by creating a new ASP.NET MVC3 project (of any kind) and a class library project. For convenience we would like to have support for Razor and all MVC3 wizards in our class library. It helps us add some basic files which are required to get everything working.
To get that support we need to modify the project file for the class library. Here is how:

1. Right-click on the class library project and choose unload project:

2. Right-click on the project file and select Edit project file

3. Add the following XML element on a new line below <ProjectGuid>

<ProjectTypeGuids>{E53F8FEA-EAE0-44A6-8774-FFD645390401};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>  

4. Save and close the project file.

5. Right-click and reload the project.

6. Add reference to “System.Web” and “System.Web.Mvc” (project settings)

You have now told Visual Studio that it should active the tooling and Razor 3 views. We can now add an area by just right-clicking on the project and select “Add Area”.

Do so and create an area with a name of your choosing. Create a controller (with an Index action) and a view for the index action. You now got your first external DLL for the MVC project.

Add a reference to the class library from the MVC3 project:

Congratulations. You now got your first “plugin” based solution. Hit F5 to run the project. I named my area “Ohh” and my controller “My” so I surf to “http://theHostNameAndPort/ohh/my” to visit my plugin controller.

Uh oh. We can visit the page alright. But the view cannot be found. It can be solved thanks to Griffin.MvcContrib. Let’s install the project in the MVC3 project:

Then open up global.asax and create a new method which maps the views:

protected void Application_Start()
{ 
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    RegisterViews();
}
protected void RegisterViews()
{
    var embeddedProvider = new EmbeddedViewFileProvider(HostingEnvironment.MapPath("~/"), new ExternalViewFixer());
    embeddedProvider.Add(new NamespaceMapping(typeof(Lib.Areas.Some.Controllers.MyController).Assembly, "BasicPlugins.Lib"));
    GriffinVirtualPathProvider.Current.Add(embeddedProvider);
    HostingEnvironment.RegisterVirtualPathProvider(GriffinVirtualPathProvider.Current);
} 

The first line creates a new file provider which loads files from embedded resources. We tell it which path it should try to provide files for. The next line simply adds our class library assembly and tell which root namespace it has. To make this work, we also have to change our view to an embedded resource:

Everything should be set now. Hit F5 and visit the area controller again. You should see the view now.

You can also try to have a break point in the class library. Debugging should work just like it used to. There is still one “problem”. We can’t modify the views at runtime, we need to recompile the project each time we adjust a view. Fortunually for us, Griffin.MvcContrib can solve that too. Open the RegisterViews method in global.asax again. Let’s add a disk file provider. It should be added before the embedded provider since the first found file will be used.

var diskLocator = new DiskFileLocator();
diskLocator.Add("~/", Path.GetFullPath(Server.MapPath("~/") + @"..BasicPlugins.Lib"));
var viewProvider = new ViewFileProvider(diskLocator);
GriffinVirtualPathProvider.Current.Add(viewProvider);  

You can now edit the views at runtime.

Building a plugin system

The last part showed you how you can put Controllers, Models and Views in class libraries. It’s quite useful as long as you don’t want to develop features in a more loosely coupled and dynamic way. This section will suggest a structure which you can use when building plugins.

The following part expects you to have used inversion of control containers in the past. The container is used to provide the application extension points to all plugins. I prefer using a container which has a module system so that each plugin can take care of all it’s registrations itself. I’m using Autofac in this article and it’s Module extension.

Let’s place all code which is shared between the plugins and the MVC3 project in a seperate class library. You could also follow follow the Separated interface pattern and only define all interfaces in that project. Thus removing all direct dependencies between the plugins and the main application.

The last thing to remember is that the default route configuration doesn’t work very well if you have controllers with the same name in different areas. To overcome that you have to manually change all route mappings to include the namespace. Also remove the “_default” from the route name.

Modified registration:

public override void RegisterArea(AreaRegistrationContext context)
{
	context.MapRoute(
		"Messaging_default",
		"Messaging/{controller}/{action}/{id}",
		new { action = "Index", id = UrlParameter.Optional },
		new[] { GetType().Namespace + ".Controllers" }
	);
}

Create a new class library named something like “YourApp.PluginBase” and add our basic extension points:

public interface IRouteRegistrar
{
	void Register(RouteTable routes);
}

Used to register any custom routes.

public interface IMenuRegistrar
{
	void Register(IMenuWithChildren mainMenu);
}

Allows the plugins to register themselves in the main application menu.

We’ll just use those two extensions in this example. Feel free to add as many extensions as you like in your own project ;)

The structure

We’ll need to have some structure for the plugins so that the can easily be managed, both during development and in production. All plugins will therefore be placed in a sub directory called “Plugins”. Something like:

ProjectName\Plugins
ProjectName\Plugins\PluginName
ProjectName\Plugins\PluginName\Plugin.PluginName
ProjectName\Plugins\PluginName\Plugin.PluginName.Tests
ProjectName\ProjectName.Mvc3

Add a new solution folder by right-clicking on the solution folder in the Solution Explorer. Do note that solution folders doesn’t exist on the disk and you’ll therefore have to manually append the folder to the location text box each time you add a new project in it.

Right-click on the Plugins folder in Solution Explorer and add a new project named “PluginName”. Don’t forget to append PluginsPlugin.Name to the location text box.

Since we this time don’t want to have any references to the plugins from the main application we have to manually copy them to the main application plugin folder. We use a post build event for that. Don’t forget to copy in all dependencies that your project has, since nothing handles that for you (the price of having no direct references from the MVC3 project to the plugins).

Complete structure:

Menu items

Keep in mind that you can not unload plugins and that they are available for all users. The easiest way to come around that is to display menu items only if the user has a role, which means that you should create one (or more) role(s) per plugin (if the plugin should not be available for all users). Griffin.MvcContrib contains a basic menu implementation which allows you to control if menu items are visible or not.

Sample code:

var item = new RouteMenuItem("mnuMyPluginIndex", "List messages", new { 
        controller = "Home", 
        action = "Index", 
        area = "Messages"});
item.Role = "MessageViewer";
menuRegistrar.Add(item);

You can later use menuItem.IsVisible when you generate the menu to determine if the item should be included or not.

Hello container

For this exercise we’ll use Autofac as the container. It contains a nifty module system which aids us in keeping the projects loosely coupled. Each plugin need to create a class which inherits from Autofac.Module and use that class to register all services which the plugin provides. Thanks to that we’ll just have to scan all plugin assemblies after any container modules and invoke them with our container.

public class ContainerModule : Module
{
	protected override void Load(ContainerBuilder builder)
	{
		builder.RegisterType<MenuRegistrar>().AsImplementedInterfaces().SingleInstance();
		builder.RegisterType<HelloService>().AsImplementedInterfaces().InstancePerLifetimeScope();
		base.Load(builder);
	}
}

The plugins are loaded from the main application using the following snippet:

var moduleType = typeof (IModule);
var modules = plugin.GetTypes().Where(moduleType.IsAssignableFrom);
foreach (var module in modules)
{
	var mod = (IModule) Activator.CreateInstance(module);
	builder.RegisterModule(mod);
}

Views during development

Since we want to be able to modify views at runtime during development we have to tell ASP.NET MVC3 where it can find our plugin views. We do this by using GriffinVirtualPathProvider and a custom file locator which looks like this:

public class PluginFileLocator : IViewFileLocator
{
    public PluginFileLocator()
    {
        _basePath = Path.GetFullPath(HostingEnvironment.MapPath("~") + @"..Plugins");
    }

    public string GetFullPath(string uri)
    {
        var fixedUri = uri;
        if (fixedUri.StartsWith("~"))
            fixedUri = VirtualPathUtility.ToAbsolute(uri);
        if (!fixedUri.StartsWith("/Areas", StringComparison.OrdinalIgnoreCase))
            return null;

        // extract area name:
        var pos = fixedUri.IndexOf('/', 7);
        if (pos == -1)
            return null;
        var areaName = fixedUri.Substring(7, pos - 7);

        var path = string.Format("{0}{1}\Plugin.{1}{2}", _basePath, areaName, fixedUri.Replace('/', '\'));
        return File.Exists(path) ? path : null;
    }
}

It simply takes the requested uri and converts it using the naming standard I described above. Everything else is taken care of by Griffin.MvcContrib.

And interesting part is that the provider is only loaded during development thanks to nifty helper class in Griffin.MvcContrib:

if (VisualStudioHelper.IsInVisualStudio)
    GriffinVirtualPathProvider.Current.Add(_diskFileProvider);

Loading the plugins

Ok. We’ve created some plugins (and their dependencies) which are copied to the bin folder with a post build event. We’ll therefore have to load them in some way. To do this we create a new class looking like this:

public class PluginService
{
    private static PluginFinder _finder;
    private readonly DiskFileLocator _diskFileLocator = new DiskFileLocator();
    private readonly EmbeddedViewFileProvider _embededProvider =
        new EmbeddedViewFileProvider(new ExternalViewFixer());
    private readonly PluginFileLocator _fileLocator = new PluginFileLocator();
    private readonly ViewFileProvider _diskFileProvider;

    public PluginService()
    {
        _diskFileProvider = new ViewFileProvider(_fileLocator, new ExternalViewFixer());

        if (VisualStudioHelper.IsInVisualStudio)
            GriffinVirtualPathProvider.Current.Add(_diskFileProvider);

        GriffinVirtualPathProvider.Current.Add(_embededProvider);
    }


    public static void PreScan()
    {
        _finder = new PluginFinder("~/bin/");
        _finder.Find();
    }

    public void Startup(ContainerBuilder builder)
    {
        foreach (var assembly in _finder.Assemblies)
        {
            // Views handling
            _embededProvider.Add(new NamespaceMapping(assembly, Path.GetFileNameWithoutExtension(assembly.Location)));
            _diskFileLocator.Add("~/",
                                 Path.GetFullPath(HostingEnvironment.MapPath("~/") + @"...." +
                                                  Path.GetFileNameWithoutExtension(assembly.Location)));

            //Autofac integration
            builder.RegisterControllers(assembly);
            var moduleType = typeof (IModule);
            var modules = assembly.GetTypes().Where(moduleType.IsAssignableFrom);
            foreach (var module in modules)
            {
                var mod = (IModule) Activator.CreateInstance(module);
                builder.RegisterModule(mod);
            }
        }
    }

    // invoke extension points
    public void Integrate(IContainer container)
    {
        foreach (var registrar in container.Resolve<IEnumerable<IMenuRegistrar>>())
        {
            registrar.Register(MainMenu.Current);
        }

        foreach (var registrar in container.Resolve<IEnumerable<IRouteRegistrar>>())
        {
            registrar.Register(RouteTable.Routes);
        }
    }
}

The plugins are now loaded and the extension points have been passed to them.

Final touch

The last thing to change is global.asax:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    _pluginServicee = new PluginService();
    RegisterContainer();
    HostingEnvironment.RegisterVirtualPathProvider(GriffinVirtualPathProvider.Current);
    _pluginServicee.Integrate(_container);
}

private void RegisterContainer()
{
    var builder = new ContainerBuilder();
    builder.RegisterControllers(Assembly.GetExecutingAssembly());
    _pluginServicee.Startup(builder);
    _container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(_container));
}

Code

The code for the samples are located in github and so is Griffin.MvcContrib. Griffin.MvcContrib can also be install from nuget.