Asp.Net MVC and Sealed Classes
I just created a project from the Asp.Net MVC default template (yes, I am a little late to this game – sorry, I have been living happily with Monorail) and ran across this in the AccountController class:
The FormsAuthentication type is sealed and contains static members, so it is difficult to unit test code that calls its members. The interface and helper class below demonstrate how to create an abstract wrapper around such a type in order to make the AccountController code unit testable.
Ha, after experiencing the pain so many times in so many ways running into these sealed / internal classes when trying to unit test, I find it somewhat satisfying to see MS have to workaround their own issues.
I am just glad that it seems like the paradigm is finally shifting at Microsoft and that maybe, just maybe, we’ll start to see less internal sealed classes and more unit testable frameworks (unit testable from my – the user’s – point of view).
Custom MonoRailHttpHandlerFactory to handle 404s gracefully
Problem
I need to display user-friendly 404 pages when a request is made for a controller that cannot be found. Monorail provides a built-in way to handle this. When faced with a request for a controller it cannot find, Monorail will look for a view named 404 in the rescues folder and render that. That is good enough for most people, I guess. It wasn''t good enough for me. I needed my 404 view to use a layout (dynamically chosen based on some configuration settings) and to display some data.
To give you an example of what I am talking about, consider an ecommerce site. When someone requests a page that doesn''t exist, you would want to show them a friendly error page that shows some featured products - some products that the user might be interested in. We don''t want to turn the user away or show a sparsely populated page when our poor, unsuspecting user types a wrong URL.
First of all, I set out to use a custom rescue controller to solve all my problems. Here is the relevant code:
[Layout("Default")]
public class RescueController : SmartDispatcherController, IRescueController
{
public void Rescue(Exception exception, IController controller, IControllerContext controllerContext)
{
if (Is404(exception))
{
Handle404();
return;
}
RenderSharedView("rescues\general", true);
}
private bool Is404(Exception ex)
{
var httpEx = ex as HttpException;
var mrEx = ex as MonoRailException;
return (httpEx != null && httpEx.GetHttpCode() == 404)
|| (mrEx != null && mrEx.HttpStatusCode.GetValueOrDefault() == 404);
}
public void Handle404()
{
BindFeaturedProductsToView();
Response.StatusCode = 404;
Response.StatusDescription = "Not found";
RenderSharedView("rescues\404");
}
private void BindFeaturedProductsToView()
{
/* bind some data to the view here */
}
}
I then append this little attribute to all of my controllers (or to the base class that all of my controllers inherit from - if you happen to have one of those...):
[Rescue(typeof(RescueController))]
That actually got me 90% of the way there. A request for an action that didn''t exist on any controller would now be handled by my custom RescueController which could attach a layout and bind any necessary data that the 404 view needs.
The Devil is in the Details
Back to the original problem of this post - I need to be able to show this same view (and render it with the same logic) when a request is made for a controller that does not exist. After digging through the Monorail source code a bit, and after a lot of trial and error,I eventually found a way to accomplish this. First of all,I had to make my Handle404 method on RescueController public so that it could be accessible via a URL. Then I ended up sub-classing MonoRailHttpHandlerFactory to handle the case of a missing controller.
public class CustomHttpHandlerFactory : MonoRailHttpHandlerFactory
{
public override IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
IHttpHandler handler = base.GetHandler(context, requestType, url, pathTranslated);
if (handler is NotFoundHandler)
{
//The default NotFoundHandler renders the 404 view in the
//rescues folder if it finds one, otherwise it throws an exception.
//We want to reuse our 404-handling logic in RescueController.
context.RewritePath("~/rescue/handle404");
return base.GetHandler(context, requestType, url, pathTranslated);
}
return handler;
}
}
Note the use of context.RewritePath. This keeps the requested URL the same in the user''s browser and simply reroutes the request internally to our RescueController. We don''t want to just do a redirect to our RescueController as this would be very bad for SEO purposes. We want to send a 404 status code to the user when they request our missing page - we just want to do it in a user-friendly way.
Gotchas
Make sure you update your web.config to use this new handler instead of the default Monorail one. Also note that I am using routing to route /rescue/handle404 to the Handle404 action on RescueController. If you were not using routing, you could change that URL to ~/rescue/handle404.castle or ~/rescue/handle404.rails depending on your configuration. Overall, I like this solution very much. It is very DRY and accomplishes everything I wanted to accomplish.
It’s Tough Times Out There
"Times are tough" -- I''ve heard that phrase ten thousand times in the past few months. I am so sick of hearing about the economy and how bad everything is. I feel like much of it is a self-fulfilling prophecy - a vicious feedback cycle that is perpetuated by how much everyone talks about it all-the-freaking-time.
Anyway, I was pulling into the parking lot at work this morning, and somebody on the radio said something to the effect of "I know times are tough, but..." and I shut the radio off right there. STFUMF - is what I thought to myself. Maybe a little harsh, but hey, its my thoughts and I can get as angry as I want in my head. I am just desperately tired of hearing about the economy - especially that one phrase - "times are tough". Are times really that tough? My wife and I have not lived our lives any differently since the start of this recession. We still go out to eat, go see movies, and generally do what we want to do. I feel like I have been largely unaffected by this recession. These were my thoughts as I got out my car and headed into work.
At 10:30, I''m walking back to my desk from the bathroom and I happen to pass my boss''s office. The HR rep for the company is in there with him. "Chad, come see a sec." I walk in, they close the door behind me. Oh shit, what did I do?
"We''ve been forced to make budget cuts and I need to cut two salaries from IT. I tried to fight it,but they told me who to cut."
Long story short,I ended up getting laid off along with 16 other people today (2 from IT). My boss looked like he was getting teary-eyed as he told me the news. He made sure to stress it had nothing to do with performance and it was simply because I made too much money. I also learned that they are getting rid of the building I work in and consolidating everyone to one building (we had 2 buildings before). Damn, I guess times really are tough.
They gave me a severance package and my wife and I have enough saved up to get us by for a while. I also think I will not have too much trouble finding another job (knock on wood). So, I am not sweating it too much. That being said, anyone out there have any leads for a .Net developer focusing on Domain-Driven Design and Test-Driven Development?
Agile Software Development from a Business Perspective
I just ran across this video presentation with software development advice from Jason Fried - from 37Signals. It's not really anything I haven't heard before, it is coming from an interesting perspective though. Here are some highlights and things that jumped out at me:
Small Iterations
He talks about one of the tenets of Agile practice, the importance of doing short iterations (rather than long month-long stints) of software releases. But he talks about it for the purpose of keeping the excitement in a project up. As far as the stakeholders in a project our concerned, this is a very important aspect. It is important to keep the stakeholders in a project interested and excited about what you are doing.
Functional Specs Suck
Functional specs don't work. They are abstractions of the software that you are actually building. Two people can read the same spec document and have completely different ideas of how the software will work. Focus instead on building your software - "doing more of what works, and less of what doesn't." With the short iterations and quick feedback cycles with your stakeholders, this leads to a much better design than any functional spec could.
Red Flag Words
A lot of "red flag words" can lead to a project going awry. "Need" is a big one. Most of the time, the client has no idea what he actually needs (see image below). Don't let the client/stakeholder steer you in the wrong direction.
"Easy" is another one -- "oh just get this-and-this done, it should be easy." Or one that I have heard myself - "Why can't you just, you know, clickity-click it and get it done?" It leads to animosity and sets people up for failure. (I know Alanna has experienced this first-hand at her job...)
He goes on to talk about a lot of other things. One of the main themes is ease of use. Is your software easy to use? Does it solve a problem in a way that makes people want to use it? Overall, its a good presentation if you are writing software for any type of business capacity. Go give it a listen.
Lightbox for YouTube Videos
On one of my recent projects, I needed to embed YouTube videos on a page using a lightbox-type plugin. Naturally, the first thing I do when confronted with something like this is to hit up Google. I ended up finding a few different solutions.
First off was Videobox - this is exactly what I wanted -- but ehh, it uses mootools. Nothing against mootools, its a great little framework - but the whole site where this is going is already using jQuery.
Next I found CeeBox - ahh - exactly what I want. This guy took Videobox and ported all the best parts over to jQuery. But, oh wait, it uses the Thickbox plugin. I forgot to mention that the site is already using the FancyBox plugin for images in other places and I'd like to keep the interface consistent.
Ok, so what I ended up doing was doing the same thing Ceebox did except with Fancybox. I only added support for YouTube because that is all I needed at the moment (YAGNI, right?).
I think the end result turned out really nice. All you need to do is include a link to the original YouTube video like so:
<a rel="fancyvideo" href="http://www.youtube.com/watch?v=rP-2ksWFk4o">Pop me up</a>
Then just wire up the script to all links with rel="fancyvideo":
<script type="text/javascript">
$(document).ready(function() {
$("a[@rel*=fancyvideo]").fancybox({
overlayShow: true,
frameWidth:640,
frameHeight:360
});
});
</script>
This has the great advantage of gracefully scaling down for browsers with javascript disabled and just providing a link to the video. For those with javascript enabled, the script automatically detects the link to YouTube (via regex) and handles embedding the video into a nice little popup window.
You can see a demo of it here and get the files here. (By the way, the video in the demo is me fighting with myself via dog weaponage. I crack myself up...)
Hello Universe
Hello to you and welcome to the mythical world of Chad''s mind. You are embarking on a journey of wonderment and mystery through one of the most bizarre things you will possibly ever come across on your journey through life - the mind of a Chad.
The mind of a Chad is characterized by many things - many of which is incomprehensible to the average human intellect. However, if you find yourself intrigued and feel like you want to peer deeper into the light, read on gentle traveler. There is much to learn.
