Tuesday, February 15, 2011

MVC3's GlobalFilters and HandleErrorAttribute

In MVC3 a GlobalFilterCollection has been added to the Application_Start. This allows you to register filters that will be applied to all controller actions in a single location. Also, MVC3 web applications now add an instance of HandleErrorAttribute to these GlobalFilters by default. This means that errors in the MVC pipeline will now be automatically handled by these attributes and never fire the HttpApplication's OnError event.

This is nice because it is another step away from the old ASP.NET way of doing things, and a step toward the newer cleaner MVC way of doing things. However, it did throw us a slight curve ball when updating CodeSmith Insight's HttpModule.

Side Note: The CodeSmith Insight MVC3 client assembly will be released next week (the week of 2/21/11).

Out With the Old

Our old HttpModule wired up to the HttpApplication's OnError event and used that to log unhandled exceptions in web applications. It didn't care if the error happened in or out of the MVC pipeline, either way it was going to bubble up and get caught in the module.

public virtual void Init(HttpApplication context)
{
   InsightManager.Current.Register();
   InsightManager.Current.Configuration.IncludePrivateInformation = true;
   context.Error += OnError;
}

private void OnError(object sender, EventArgs e)
{
   var context = HttpContext.Current;
   if (context == null)
       return;

   Exception exception = context.Server.GetLastError();
   if (exception == null)
       return;

   var abstractContext = new HttpContextWrapper(context);
   InsightManager.Current.SubmitUnhandledException(exception, abstractContext);
}

However, now the MVC HandleErrorAttribute may handle exceptions right inside of the MVC pipeline, meaning that they will never reach the HttpApplication and the OnError will never be fired. What to do, what to do...

In With the New

Now we need to work with both the attributes and the HttpApplication, ensuring that we will catch errors from both inside and outside of the MVC pipeline. This means that we need to find and wrap any instances of HandleErrorAttribute in the GlobalFilters, and still register our model to receive notifications from the HttpApplications OnError event.

The first thing we had to do was create a new HandleErrorAttribute. Please note that this example is simplified and only overrides the OnException method. If you want to do this "right", you'll have to override and wrap all of the virtual methods in HandleErrorAttribute.

public class HandleErrorAndReportToInsightAttribute : HandleErrorAttribute
{
   public bool HasWrappedHandler
   {
       get { return WrappedHandler != null; }
   }

   public HandleErrorAttribute WrappedHandler { get; set; }

   public override void OnException(ExceptionContext filterContext)
   {
       if (HasWrappedHandler)
           WrappedHandler.OnException(filterContext);
       else
           base.OnException(filterContext);

       if (filterContext.ExceptionHandled)
           InsightManager.Current.SubmitUnhandledException(filterContext.Exception, filterContext.HttpContext);
   }
}

Next we needed to update our HttpModule to find, wrap, and replace any instances of HandleErrorAttribute in the GlobalFilters.

public virtual void Init(HttpApplication context)
{
   InsightManager.Current.Register();
   InsightManager.Current.Configuration.IncludePrivateInformation = true;
   context.Error += OnError;

   ReplaceErrorHandler();
}

private void ReplaceErrorHandler()
{
   var filter = GlobalFilters.Filters.FirstOrDefault(f => f.Instance is HandleErrorAttribute);
   var handler = new HandleErrorAndReportToInsightAttribute();

   if (filter != null)
   {
       GlobalFilters.Filters.Remove(filter.Instance);
       handler.WrappedHandler = (HandleErrorAttribute) filter.Instance;
   }

   GlobalFilters.Filters.Add(handler);
}

In Conclusion

Now when we register the InsightModule in our web.config, we will start capturing all unhandled exceptions again.

<configuration>
 <configSections>
   <section name="codesmith.insight" type="CodeSmith.Insight.Client.Configuration.InsightSection, CodeSmith.Insight.Client.Mvc3" />
 </configSections>
 <codesmith.insight apiKey="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" serverUrl="http://app.codesmithinsight.com/" />
 <system.web>
   <customErrors mode="On" />
   <httpModules>
     <add name="InsightModule" type="CodeSmith.Insight.Client.Web.InsightModule, CodeSmith.Insight.Client.Mvc3"/>
   </httpModules>
 </system.web>
</configuration>

Friday, January 28, 2011

How to Learn ExtJS

Ever since CodeSmith Insight was featured on the Sencha Product Spotlight I have been getting a lot of questions about ExtJS. Specifically how to start learning it, and what tools we recommend using. Well rather than respond to these inquiries one email at a time, I thought it might be a good idea to throw up a blog post about how to learn ExtJS.

I hope this helps you get started, but always feel free to contact me with any additional questions you have.

Start with the Samples

I think the best way to start learning ExtJS is to reverse engineer some of the official ExtJS samples. I like learning by example, so I found that to be a great starting point. After I had a grasp of the fundamentals (their object orientation, their standard configuration properties, etc), I was able to start authoring my own components. I suggest starting with the window examples, taking a look at the form examples, and then really getting a feel for the full application layouts by exploring the feed viewer.

Also, the most important and best thing that you can do: keep the ExtJS API open in another window at all times!

Try using the ExtJS Designer

The Ext Designer is a visual designer for creating ExtJS components. I did not have access to this back when I started working with ExtJS, but in retrospect, I wish that I had. Recently I had the opportunity to help another company get started with ExtJS where they used the designer, and I can endorse that it's a great tool. It allows you to visually drag drop and resize components, such as elements on windows and panels, and helps you get an image of what you are creating without having to render your classes in the browser every step of the way.

The designer is great for two reasons: 1) you can see what you are doing, which makes it much easier to learn what ExtJS configuration properties do, and 2) you don't have to worry about one bad value or syntax error crashing your whole page every time you are trying to learn a new ExtJS component.

Debugging Tools

Firebug for Firefox is still (in my opinion) the best debugging tool around. It is all around responsive, it includes the JSON viewer, it's JavaScript console has auto complete, and you can edit DOM elements inline with ease.

The Google Chrome Development Tools (not Firebug Lite, but the actual built in tools themselves) have really grown into a usable set of tools since they were released two years ago. It finally competes with Firebug for responsiveness and available features. While it does not have the JSON viewer, it does offer a very useful and unique local storage viewer.

Wednesday, December 22, 2010

Ext.ux.JSONP v2.0

The ExtJS library does include a JSONP component, but I found it to be lacking a very important piece of functionality.

The Problem: No Error Handler

Anyone who know's anything about JSONP is immediately going to call attention to the fact that JSONP isn't capable of supporting error handling by design. While that is true, it doesn't mean that we don't have to handle errors. One of the more well known jQuery plugins for JSONP includes a callback method for "complete" which enables you to know when a request failed.

One additional nitpick that I had with the ExtJS JSONP component what that I did not feel it was coded in a very Object Orient fashion, but that's probably just a personal gripe.

The Solution: v2.0

I rewrote the Ext.ux.JSONP component to include additional parameters that match up with a normal Ext.Ajax.request. This means that you can access handlers for success, failure, and callback.

Also, I made the code much more OO.

Download

Ext.ux.JSONP v2.0

Examples

// Old
Ext.ux.JSONP.request('http://api.flickr.com/services/feeds/photos_public.gne', {
   callbackKey: 'jsoncallback',
   params: {
       format: 'json',
       tags: Ext.fly('search-value').dom.value,
       tagmode: 'all',
       lang: 'en-us'                            
   },
   callback: updateResults
});

// New
Ext.ux.JSONP.request({
   url : 'http://api.flickr.com/services/feeds/photos_public.gne',
   params: {
       format: 'json',
       tags: Ext.fly('search-value').dom.value,
       tagmode: 'all',
       lang: 'en-us'                            
   },
   success : this.updateSuccess,
   failure : this.updateFailure,
   callback : this.updateCallback,
   scope : this
});

Thursday, November 11, 2010

Migration in Progress

Welcome to my new blog, TomDuPont.NET

I am currently in the process of migrating my old blog to this site. I have been actively blogging since 2008, so getting all of those entries moved over is a little daunting, but I am getting there!
For now, assuming you were looking for content, please visit my CodeSmith Tools Blog.

Wednesday, October 13, 2010

Insight Launch Party a Huge Success

The CodeSmith Insight Launch Party was a huge success. Thanks to the almost one hundred people that came out and partied with us. It was a blast, thank you all so much for coming out!

We did a 15 minute demo of Insight before we gave out prizes. To show that we had nothing up our sleeves the demo was completely unrehearsed and using our live production servers. I am pleased to say that it went off without a hitch! We even had some surprise audience participation as people in the crowd sent in emails to our demo instance during the presentation; not only was that fun, but it also really helped demonstrate the power and versatility of Insight in real time.

At the end of the demo we actually went overtime doing Q&A about Insight. Not only did we get asked a ton of questions from all around the room, but honestly they were great questions! This was flattering for two reasons: 1) As the speaker those questions told me that the audience was paying attention, and that what I was saying was intelligible. 2) As a developer it was great to see other developers thinking about use cases for Insight, and then being able to be one step ahead of the game and say "Yes, we support that."

Here are a few of the questions we were asked about Insight:

  • Can Insight report handled exceptions?
    • Yes. Insight's client API allows you to create cases for anything at anytime, and provides a series of CreateCase method overrides to make it especially easy to create a report from a caught exception.
  • Can Insight be used to submit feedback without reporting errors?
    • Yes. Insight can be configured to not report unhandled exceptions, and then you can still use the feedback forms and API to create and submit cases based only on user feedback.
  • Does Insight integrate with Microsoft Exchange?
    • Yes. Insight can send and receive email from almost any POP3 or IMAP server, that includes Microsoft Exchange.
  • Do you offer an installed solution?
    • This is the only question to which I had to answer "no." However, we are very interested in offering installed solutions in the future, so if you are interested in this please contact us directly (at sales@codesmithtools.com) and we will be more than happy to try and work with you!

In summary, the party was a blast. I think everyone had fun; I know I did. Insight is now released and available to log exceptions for developers everywhere. Also, feel free to check out the party photos we posted up on Facebook. (If you are in one and want to be tagged just let us know!)

Thanks again,
Tom DuPont

On behalf of,
The CodeSmith Tools

Friday, October 8, 2010

MVC2 Unit Testing, Populating ModelState

I love how testable ASP.NET MVC is, I also love MVC2's model validation. However when trying to unit test a controller method that used the ModelState, I quickly learned that the ModelState is not populated when just newing up a Controller and calling one of its public methods. As usual, I think this is best narrated by example:

Example Model and Controller

public class PersonModel
{
  [Required]
  public string Name { get; set; }
}

public class PersonController : Controller
{
  [AcceptVerbs(HttpVerbs.Get)]
  public ViewResult Register(Person person)
  {
    return View(new PersonModel()); 
  }

  [AcceptVerbs(HttpVerbs.Post)] 
  public ViewResult Register(Person person) 
  {
    if (!ModelState.IsValid) 
      return View(model); 

    PersonService.Register(person);
    return View("success");
  }
}

Example of the Problem

[Test] 
public void RegisterTest() 
{
  var model = new PersonModel { Name = String.Empty }; // This is model is invalid.
  var controller = new PersonController(); 
  var result = controller.Register(model);

  // This fails because the ModelState was valid, although the passed in model was not. 
  Assert.AreNotEqual("success", result.ViewName);
}

Solution

Other solutions I have come across were adding the errors to the model state manually, or mocking the ControllerContext as to enable the Controller's private ValidateModel method. I didn't like the former because it felt like I wasn't actually testing the model validation, and I didn't like the latter because it seemed like a lot of work to both mocking things and then still have to manually expose a private method.

My solution is (I feel) pretty simple: Add an extension method to the ModelStateDictionary that allows you to pass in a model, and it will then validate that model and add it's errors to the dictionary.

public static void AddValidationErrors(this ModelStateDictionary modelState, object model)
{
  var context = new ValidationContext(model, null, null); 
  var results = new List<ValidationResult>(); 
  Validator.TryValidateObject(model, context, results, true);

  foreach (var result in results) 
  { 
    var name = result.MemberNames.First(); 
    modelState.AddModelError(name, result.ErrorMessage);
  }
}

Example of the Solution

[Test] 
public void RegisterTest() 
{
  var model = new PersonModel { Name = String.Empty }; // This is model is invalid.
  var controller = new PersonController(); 

  // This populates the ModelState errors, causing the Register method to fail and the unit test to pass.
  controller.ModelState.AddValidationErrors(model);
  var result = controller.Register(model);
  Assert.AreNotEqual("success", result.ViewName); 
}

Thursday, September 30, 2010

Oct 12th is (Insight) Party Time!

We are very excited about our upcoming CodeSmith Insight Launch Party. We rented out the Addison Convention Center, ordered ten old school arcade machines, bought over $1,000 in prizes, booked flights for the entire CodeSmith Tools team is come in from around the country, and now we are counting down until party time!

However, there have been some questions (and even concerns) about party which I feel need to be addressed.

First: This party is NOT a time-share-condo-ploy.

We are going to speak about Insight for 10 minutes, and field questions for another 5 minutes. This is a three hour party, we are only going to do "business" for 15 minutes total. That is all.

Why? It's a PARTY. We have been working our butts off for almost two years developing Insight, now we want to CELEBRATE (good times, come on)! Seriously, we are renting arcade games because they are FUN.

Second: What does "over $1,000 in prizes" mean?

Well first of all, that number does not even include:

  • Free T-Shirts
  • Free Pizza Hut Pizza
  • 50% Lifetime Discount for all Attendees

The $1,000 in prizes is made up of the following:

  • Apple IPad
  • Crucial RealSSD C300 128GB
  • Microsoft X-Box 360
  • Nintendo Wii

...and there will be additional door prizes! THAT is what "over $1,000 in prizes" means.

Third: JOIN US FOR THE FESTIVITIES!

Please feel free to invite your coworkers, your friends, even your mother. (Seriously, my Mom will be there!)

When
Tuesday, October 12th
6:00pm - 9:00pm

Where
Addison Conference Center‎
15650 Addison Road
Addison, TX‎ ‎75001

RSVP, and I will see you there!

Tom DuPont
Vice President, CodeSmith Tools 

Real Time Web Analytics