Posts tagged MVC

ASP.NET MVC REST API WINDSOR CONTROLLER FACTORY

I’m using the ASP.NET MVC REST Toolkit for a REST API I’m building for a mobile infrastructure. Long story short, it will be responsible for service hundreds of thousands (if not millions) of users through their mobile phones and rich applications (Android, iPhone and eventually the WP7 Phones).

The one thing that the Toolkit did not provide out of the box was a way to use a container in the controller factory. Therefore I altered the ResourceControllerFactory to use Windsor Container as its default container. The code works great and I’m able to utilize dependency injection all throughout my app now.

The code for this is below:

//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------

using Castle.MicroKernel;

namespace System.Web.Mvc.Resources
{
    using System.Collections.Generic;
    using System.Globalization;
    using System.Net;
    using System.Net.Mime;
    using System.Text;
    using System.Web.Routing;

    /// <summary>
    /// Specialized ControllerFactory that augments the base controller factory to make it RESTful - specifically, adding
    /// support for multiple formats, HTTP method based dispatch to controller methods and HTTP error handling
    /// </summary>
    public class ResourceControllerFactory : IControllerFactory
    {
        // Note: This has been changed from the regular controller factory provided
        // by the ASP.NET Team so that we can use Windsor Container to resolve
        // container dependencies.

        readonly IKernel _kernel;
        const string restActionToken = "$REST$";

        public ResourceControllerFactory(IKernel kernel)
        {
            _kernel = kernel;
        }

        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            IController ic = _kernel.Resolve<IController>(controllerName.ToLowerInvariant() + "controller");
            Controller c = ic as Controller;
            if (c != null && WebApiEnabledAttribute.IsDefined(c))
            {
                IActionInvoker iai = c.ActionInvoker;
                ControllerActionInvoker cai = iai as ControllerActionInvoker;
                if (cai != null)
                {
                    c.ActionInvoker = new ResourceControllerActionInvoker();

                    string actionName = requestContext.RouteData.Values["action"] as string;
                    if (string.IsNullOrEmpty(actionName))
                    {
                        // set it to a well known dummy value to avoid not having an action as that would prevent the fixup
                        // code in ResourceControllerActionInvoker, which is based on ActionDescriptor, from running
                        requestContext.RouteData.Values["action"] = restActionToken;
                    }
                }
            }
            return ic;
        }

        public void ReleaseController(IController controller)
        {
            _kernel.ReleaseComponent(controller);
        }

        // This ActionInvoker allows us to dispatch to a controller when no action was provided by the routing
        // infrastructure, but the information is available in the request's HTTP verb (GET/PUT/POST/DELETE)
        class ResourceControllerActionInvoker : ControllerActionInvoker
        {
            public ResourceControllerActionInvoker()
            {
            }

            protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
            {
                if (actionName == restActionToken)
                {
                    // cleanup the restActionToken we set earlier
                    controllerContext.RequestContext.RouteData.Values["action"] = null;

                    List<ActionDescriptor> matches = new List<ActionDescriptor>();
                    foreach (ActionDescriptor ad in controllerDescriptor.GetCanonicalActions())
                    {
                        object[] acceptVerbs = ad.GetCustomAttributes(typeof(AcceptVerbsAttribute), false);
                        if (acceptVerbs.Length > 0)
                        {
                            foreach (object o in acceptVerbs)
                            {
                                AcceptVerbsAttribute ava = o as AcceptVerbsAttribute;
                                if (ava != null)
                                {
                                    if (ava.Verbs.Contains(controllerContext.RequestContext.GetHttpMethod().ToUpperInvariant()))
                                    {
                                        matches.Add(ad);
                                    }
                                }
                            }
                        }
                    }
                    switch (matches.Count)
                    {
                        case 0:
                            break;
                        case 1:
                            ActionDescriptor ad = matches[0];
                            actionName = ad.ActionName;
                            controllerContext.RequestContext.RouteData.Values["action"] = actionName;
                            return ad;
                        default:
                            StringBuilder matchesString = new StringBuilder(matches[0].ActionName);
                            for (int index = 1; index < matches.Count; index++)
                            {
                                matchesString.Append(", ");
                                matchesString.Append(matches[index].ActionName);
                            }
                            return new ResourceErrorActionDescriptor(controllerDescriptor, HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, "Error dispatching on controller {0}, conflicting actions matched: (1)", controllerDescriptor.ControllerName, matchesString.ToString()));
                    }
                }
                return base.FindAction(controllerContext, controllerDescriptor, actionName) ??
                    new ResourceErrorActionDescriptor(controllerDescriptor, HttpStatusCode.NotFound, string.Format(CultureInfo.CurrentCulture, "Error dispatching on controller {0}, no actions matched", controllerDescriptor.ControllerName));
            }

            // This class is used when we don't find an ActionDescriptor or find multiple matches
            // in this case we want to return an error response but throwing an HttpException from
            // FindAction bypasses the InvokeExceptionFilters, so instead we throw in this custom ActionDescriptor
            class ResourceErrorActionDescriptor : ActionDescriptor
            {
                ControllerDescriptor controllerDescriptor;
                string message;
                HttpStatusCode statusCode;

                public ResourceErrorActionDescriptor(ControllerDescriptor controllerDescriptor, HttpStatusCode statusCode, string message)
                {
                    this.message = message;
                    this.statusCode = statusCode;
                    this.controllerDescriptor = controllerDescriptor;
                }

                public override string ActionName
                {
                    get { return restActionToken; }
                }

                public override ControllerDescriptor ControllerDescriptor
                {
                    get { return this.controllerDescriptor; }
                }

                public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)
                {
                    HttpException he = new HttpException((int)this.statusCode, this.message);
                    ResourceErrorActionResult rear;
                    if (!WebApiEnabledAttribute.TryGetErrorResult2(controllerContext.RequestContext, he, out rear))
                    {
                        rear = new ResourceErrorActionResult(new HttpException((int)this.statusCode, this.message), new ContentType("text/plain"));
                    }
                    return rear;
                }

                public override ParameterDescriptor[] GetParameters()
                {
                    return new ParameterDescriptor[0];
                }
            }
        }
    }
}

You can easily replace the Windsor stuff with StructureMap, NInject, Autofac, etc.

Enjoy.

My CODE Magazine Article Is Live

My first article with CODE Magazine is live. You can read it here. I cover ASP.NET MVC with the Spark View Engine. I cover a wide range of things and go in depth with examples with topics that include:

  • Inline Code
  • Element Constructs
  • Partials
  • Layouts
  • and more …

I hope you enjoy it! I enjoyed writing it.

A big thanks goes out to Chris G. Williams (twitter)for doing a stellar job on the tech editing.

Read it Here

Medium Trust is a PITA

There. I said it. Medium Trust is PITA. I wont be surprised if someone replies with  one of the following:

“Well, not really, you only have to do x, y and z and your code is compatible with medium trust environments”

or

“It’s there (Medium Trust) because code needs boundaries within controlled scenarios such as hosting environments.. “

Blah. Blah. Blah. You’re both right. However, it’s still a PITA. Why? In the past 4 months I’ve spent countless hours figuring out why my app is broken in a medium trust environment. I use certain tools to make my life easier (such as Castle Active Record, Windsor, ASP.NET MVC, Spark, etc) yet a lot of them assume full trust. They do have options to get them running under partial/medium trust but in my experience the debugging of these tools in said environments sucks.

Needless to say, I just got done debugging for 2.5 hours why my Spark MVC site would not precompile my views. The error was “No Input could be found". WTH!?! Finally, after walking through my app line by line I found the following in a Spark view:

 

${Html.ActionLink("Track Admin", "index", new { controller = "track" })} ${Html.ActionLink("Speaker Admin", "index" , new {controller="speaker"})}

 

The problem is that I had deleted those controllers and moved that part of the app. Once I removed the code, it recompiled fine. Completely frustrating and the root cause of this is Medium trust. I just spent 2.5 hours working on a debugging issue for Medium trust when I could be creating my app for a mobile start up company that I’m heading up.

Long story short : I’m not a fan of Medium Trust. I don’t know many people who are.

How Can we Get Around It?

I’m not sure we can in the .NET space. Other dynamic languages don’t seem to have these issues, but they do run into odd edge cases at runtime which can be even more of a pain to handle. So at this point, the only answer I have is to recommend any open source or commercial vendor to test their product on a lock down medium trust environment and provide very clear and concise instructions for how to get their product to run under medium trust.

New DimeCast: Spark Partials Part 2

I have another DimeCast up on DimeCasts.net.

More Info: In this episode we are going to continue looking at how to use Partials within the Spark View Engine.

In this episode we will take a deeper look at partials and learn more about their _undocumented_ features. We will also be learning how partials will allow us to create clean, compartmentalized and well organized code.

Check it out here.