Custom Errors in ASP.Net MVC: It couldn't be simpler, right?

parallaxing 404 at github

See til.secretgeek: MVC custom errors for some serious guidance. The lengthy article below is devilish trickery from a time when I was in a dark place.

Cool looking 404 pages are the new hotness. Github has an amazing parallaxing 404 page that allegedly cost more than any other feature on their site.

For a lot of sites, the 404 page is the most visited page, so it's worth getting it right.

The website for my new product, NimbleText, uses asp.net mvc. A framework I really enjoy. The gu wrote it on a plane. Before takeoff.

One of the more voodoo aspects of getting NimbleText.com into production was setting up a succesful custom 404 page. Here's what I came up with: check it out.

Some of the articles out there that cover custom errors in asp.net MVC seemed to be a little bit confused about exactly what is going on, many are out of date or incomplete and some are downright misleading.

So once and for all I want to give a definitive guide to error handling in asp.net MVC.

Here we are. Just eleven simple steps to follow for amazing results.

First, map a catch-all route in global.asax, at the end of your other routes. E.g.

routes.MapRoute(
   "404-PageNotFound",
   "{*url}",
   new { controller = "StaticContent", action = "PageNotFound" }
);

Second, create an Error Controller, like this:

public class ErrorController : Controller
{
   [AcceptVerbs(HttpVerbs.Get)]
   public ViewResult Unknown()
   {
     Response.StatusCode = (int)HttpStatusCode.InternalServerError;
     return View("Unknown");
   }

   [AcceptVerbs(HttpVerbs.Get)]
   public ViewResult NotFound(string path)
   {
     Response.StatusCode = (int)HttpStatusCode.NotFound;
     return View("NotFound", path);
   }
}

Third and fourth -- create custom views to handle the Unknown and NotFound actions above.

Fifth, create a page called Custom404.htm and add it to the root of your application. Use it to display a helpful, edgy and hopefully cool message. But don't be too edgy.

Sixth, add this to web.config, inside the system.web node:

<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
   <error statusCode="404" redirect="/Custom404.htm" />
</customErrors>

Seventh, add a httpErrors element inside the system.webServer node:

<httpErrors>
   <error statusCode="404" subStatusCode="-1" path="/custom404.htm" responseMode="Redirect" />
</httpErrors>

Eighth, inside your existing controllers, if there is no data to serve (e.g. if someone asks for a UserId that doesn't exist) then use this snippet of code:

throw new HttpException(404);

Ninth, inside Global.asax, look for the RegisterGlobalFilters method (it will be called during Application_Start), and add another global filter, to handle any specific exceptions in your code, like this:

filters.Add(new HandleErrorAttribute
    {
        ExceptionType = typeof(FileNotFoundException),
        View = "Custom404", // Custom404.cshtml is a view in the Shared folder.
        Order = 2
    });

Tenth, add a view called Custom404.cshtml under views/shared.

And, eleventh, take care to handle the global application error event, capturing and logging any errors, before redirecting to the views you're after:

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Response.Clear();
    HttpException httpException = exception as HttpException;
    if (httpException != null)
    {
        string action;
        switch (httpException.GetHttpCode())
        {
            case 404:
                // page not found
                action = "HttpError404";
                break;
            case 500:
                // server error
                action = "Internal500";
                break;
        default:
            action = "General";
            break;
        }
        // clear error on server
        Server.ClearError();
        Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
    }
}
unexpected error

Good.

Now your website is well and truly borked.

Every request will bounce around your application like a demented pinball tripping on acid.

When an error happens, a bunch of different code modules will go to war.

The victorious code module will tear out the entrails of all those who oppose it, and throw them in the visitor's face.

You'll get a different error handler depending on what version of IIS you have, what version of MVC you're using, whether you've deployed in debug or release, whether you're visiting locally or remotely, whether it's sunny or raining.

Your server will be reduced to a pile of smouldering rubble from which reboot is impossible.

But if you want to get truly weird, try setting up a custom 403 page. That's about three times as odd.

Or, switch to Dos on Dope*. Life will be simple again.

Sorry I didn't really have time to write a definitive guide to this stuff. Error handling in MVC is crazy man, I'm busy fighting a clandestine war against hackers and crackers and bots and beuracrats: I can't be documenting that kind of mystery wrapped in an enigma.

If someone does write a complete guide to error handling in MVC, that includes the meaning and interaction behind each of these snippets, plus how the interplay between IIS, asp.net and MVC truly works, then I will happily update this article to include a link to it, right up top.


* Don't switch to Dos On Dope.

See til.secretgeek: MVC custom errors for some serious guidance. The article above is devilish trickery from a time when I was in a dark place.

 

Anatomy of a Domain Hijacking, part 2: The Website Who Came In From The Cold

When secretGeek.net was taken I swore a solemn oath to myself:

My relentless campaign of jokes and nonsense will not be stopped.

And now, just a couple of long weeks later, here I am, happy to report I'm back in control of secretGeek.net.

Right when I was ready to migrate over to leonbambrick.com, I got an email from the Russian registrar, Regtime Ltd, saying:

Sorry  for answer delay. Domain was transferred onto you account.

The number one thing, I think, that helped get the site back was when a good friend, Madina, translated a lengthy email into fluent Russian for me to send to the Russian Registrar.

She re-structured the email to put the sob-story up front, all about how much personal meaning this site has for me, and the positive effects it has had on my life. I think that did the trick.

So what did we learn?

I learnt that passwords at google can be brute forced, if pop is enabled. This can be sped up by use of multiple IP addresses, or a botnet.

That's the most likely way they got access to my account. My password was 'good' by gmail standards but is now 'freaking solid' by any standard.

And I've turned on 2-step verification, plus all the other recommendations from part 1.

Thanks for the encouragement and support. It was dark times, but now the nonsense can continue.

 

Anatomy of a Domain Hijacking, part 1

Two weeks ago I'd never heard the term 'Domain Hijacking'. Right now, I'm in the middle of a fight to regain control of my hijacked domain, secretGeek.net. It's not an easy fight, I haven't yet won, and I may never win.

If you have any information that could help me get control of my domain again please leave a comment, or tweet me (@secretgeek), or get in touch via my (now re-secured) email address, leonbambrick@gmail.com

From Russia with Love

On Monday 9th May, I checked my gmail account at around 3:40 in the afternoon, and I was confronted with a dark red message at the top of the screen (in the area where you normally see messages like 'Your email has been sent'). The message said:

Warning: we believe your account was recently accessed from: Russia. Show details and preferences | Ignore

I clicked on 'show details and preferences', and a new window opened with this message:

(click to see the message in context -- [note that my own ip addresses are redacted])

This was definitely not me. At 4:19 AM, and 5:40am I had been far too busy being fast asleep, preparing for a big week, to get to Russia and back for some casual email reading. So someone had infiltrated my email. The freaking out sensation began immediately. I couldn't move. I was frozen completely still.

I followed google's advice and immediately changed my password, then notified my wife. My mind was racing as to what the implications could be.

A little voice told me to check the trash. I was really hesitant, I think I knew the trash would contain something I didn't want to see.

In the trash I found two messages, received at 5:45am.

The content of the messages was to explain that my domain, secretGeek.net, had been succesfully transferred from my registrar, GoDaddy, to a Russian registrar, Regtime Ltd.

I felt completely unreal, like this was a dream, or a prank. I logged into my GoDaddy account to check on my domains. They all still appeared to be there. I took a few deep breaths. I felt it must be all just a daydream. I read the list carefully. No -- secretGeek.net was missing from the list. All of the others were still there.

I started sending emails to GoDaddy. I replied to the emails I'd found in the trash, telling them that it was a mistake, the result of my email being violated.

I lodged support requests with GoDaddy. When I went back to the trash to read the deleted emails again I found there were new messages in the trash. Emails from godaddy in response to my support requests had been automatically deleted. This was *weird*. It was a few minutes before the penny dropped.

I looked in my gmail filters and there were two new ones. The two new filters were designed to delete any messages containing the words 'GoDaddy' or 'Webnames'. (I later found that 'Webnames.ru' is the trading name of 'Regtime Ltd', the Russian registrar holding secretGeek.net)

This was starting to become very 'real'. This wasn't some kind of misunderstanding. It was definitely a thing. It was a deliberate, targeted attack, by a person or people who knew what they were doing and had either done this thing before or had thought it through very carefully.

I turned to my friend, Joe Cooney, to show him what I'd found. He was equally gobsmacked. He told me to call GoDaddy on their support number and see what they said.

A whois request on secretGeek.net showed that my name servers were still listed, but the registrar was Regtime ltd, and the contact information was all blank.

The support guy at GoDaddy told me to contact the gaining registrar and find out what their policy is for transfer disputes. He told me there was nothing GoDaddy could do (turns out this was bad advice, by the way)

I was busy worrying about what else was in danger. My wife was already on the task of changing every password, keycode, and security question associated with all of our bank accounts, credit cards, online identities, home pcs etc, and checking at each of these places for any trace of unusual activity.

In order to send a support request to the Russian registrar, I had to sign up as a member (and I noted with some distaste that they sent me my new password in plain text.) I sent them multiple emails explaining what happened. Then I re-sent each of those messages, in Russian this time, translated by translate.google, and including the original text.

I worked late that day to make up for the lost time and somehow managed another check-in before I went home.

That night I called GoDaddy again, hoping to get more help. By now I'd read about the similar case of David Airey where a gmail vulnerability led to a domain hijacking, which also involved godaddy. This time I got a more helpful support staff member. She told me to fill out the 'dispute on transfer away' form at GoDaddy, which I promptly did. This meant that the right people at godaddy would be notified, and they'd contact the Russian registrar to request the domain be returned.

Wrapping up part 1.

While there's more that's happened since, things haven't really progressed since that day at all.

I still haven't regained control of the domain. I'm waiting on word from the Russian registrar. I remain hopeful that any minute now I'll get an email from them saying "Ah, we're sending back your domain!" -- but my hope is fading.

The name servers haven't been changed, so my content continues to be served up as always. If it does go down, the plan is to launch a new and improved secretGeek at LeonBambrick.com. Announcements or news will be via twitter (@secretGeek) or from my business website, NimbleThing.net.

If you have any information that could help me get control of my domain again please leave a comment, or tweet me (@secretgeek), or get in touch via my (now re-secured) email address, leonbambrick@gmail.com

In the next part of the story I want to discuss gmail security, all of the possible ways I could've been hacked, and the one I think is most likely. But I can't wait until then to put out the following checklists. These are steps that you can do to protect your email account and to protect your domains:

What can you do to stop this happening to you?

Protect your email via:

  • Change your password often. For example, right now.
  • Make your password very strong.
  • In gmail: disable POP.
  • Ensure your password recovery options are as strong as your password.
  • Make sure 'always use https' is selected.
  • In gmail: check for unusual activity (it's available at the foot of the screen).
  • In gmail: don't let any other apps be associated with your account.
  • Use two-step authentication.

Protect your domains via:

  • Pay for private domain registration.
  • Pay for enhanced transfer protection. (e.g. at GoDaddy they call it 'Deadbolt' protection, and it means they require you to send them identification before they'll approve a transfer.)
 

secretGeek.net domain has been stolen. The site may go down.

The short story:

A person with a Russian IP address broke into my email account and used that access to transer ownership of the secretGeek.net domain to a Russian registrar, webNames.ru.

The full story is jam packed with intrigue, daring adventure, black ops helicopters, sexy double agents, Ukrainian php developers, and a liberal dash of nerdy security best practices.

I will tell the full story once I have secured my domain again.

In the meantime, if your beloved secretGeek.net is suddenly replaced by a spammy porn site or variant thereof, do not despair. Follow me on twitter (@secretGeek) or head to my business website (NimbleThing.net) where (if necessary) I will mirror the original secretGeek content and continue blogging until I've taken charge of the domain again.

 

Boring article about fixing a 'Login failed... untrusted domain' issue when connecting to SQL Server.

This will be the most boring and dry technical post I've ever written. I promise. I'm only writing it down because I hear Jeff Atwood's voice in my head telling me that if I haven't shared the solution then there's almost no value in solving it.

So here was the problem...

On one of my machines, in recent months, anytime I tried to run an asp.net application that attempts to connect to a SQL Server database I receive this error:

Login failed. The login is from an untrusted domain and cannot be used with Windows authentication.

This is in a scenario where everything is local: I'm on a home computer, using a local database server. And it doesn't matter if I'm using IIS, webdevserver, or IIS Express. It doesn't matter if I'm trying to connect to SQL Express or SQL Server 2008 r2. I can enter the credentials using the machine name or IP address. The result is the same.

Looking in the Windows Event Viewer I see:

SSPI handshake failed with error code 0x8009030c, state 14 while establishing a connection with integrated security; the connection has been closed. Reason: AcceptSecurityContext failed. The Windows error code indicates the cause of failure. [CLIENT: ].

Most of the 'solutions' online were not applicable because they involve domain issues. This is just a local machine.

One particular forum message involved a guy with a similar setup to me. He solved the problem for himself, but left behind only this very abbreviated and somewhat cryptic explanation:

Solved. Traced the prob. to my net setup. The clue was this a msg in the Win System Log from Lsasrv about the target name and it showed the fqdn. Did some checks and the fqdn didn't look right, cleaned up my network config (I had customized it for another project), and now it's workin.

The 'fqdn' is the fully qualified domain name. I found three different ways to find my fqdn, and one of them disagreed with the others! So this was a smoking gun.

Method 1 for finding the fqdn: When I right clicked on "computer" in the start menu and looked at the properties, the 'full computer name' was reported as 'Leon_xps'.

Method 2 for finding the fqdn: When I ran "ipconfig /all" and got these values for Host Name and Primary Dns Suffix:

   Host Name . . . . . . . . . . . . : Leon_xps
   Primary Dns Suffix  . . . . . . . : 

(Add them together and you get a fqdn of just 'Leon_xps'

Method 3 for finding the fqdn: In cmd when I ran 'ping -a Leon_xps' I saw a very unexpected result!

It said:

Pinging leonx_xps [192.168.1.2] with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.

Notice that it's changed the name 'leon_xps' into 'leonx_xps' and come up with the ipaddress of '192.168.1.2'. instead of what I expected (such as a loopback address, 127.0.0.1... or some IPv6 nonsense)

There's only one kind of alchemy I know of for turning a good name into a crazy ipaddress: and that's the magic of the HOSTS file.

So I looked in there for 'leonx_xps' and I found it. Found this crazy nonsense line, probably added by myself during some other wild-goose-chase for a solution to some other messed up problem:

192.168.1.2 leonx_xps leon_xps

Once I'd commented out that rule, the asp.net application immediately started to connect succesfully to SQL Server 2008 r2. And I can move forward with my testing of Massive and Dapper.

But just mentioning the probable cause of this (not cleaning up during an earlier problem-panic) I remember that earlier tonight, while frantically trying to fix this problem I added some stupid rules to the windows firewall. I had better go and remove them now before my computer gets owned. Hey wait a moment!? Why is my CPU at 100%? Be right back...

 

Coding While You Commute

coding on the bus

I gave a talk at Brisbane's alt.net recently. You can download the slides here (1.5M zipped) or:

 

View the 97 slides online here

                (use arrow keys to advance)

 

The theme was 'Coding on the Bus' -- the optimisations and approach I've used for cranking out a dozen mini projects on my daily commute in the last year.

A bus is a seemingly counter-productive environment. As I say in one of the slides:

If your workplace was like a bus: everyone would quit.

And yet I'm producing a lot of stuff while on that short ride, definitely pushing the limits of my abilities. So this talk was an attempt to extract all the lessons I could from this real life productivity success story.

TL;DR;

Here's the quick version:

#1 Internet is bad
#2 Keep it damn simple
#3 Value your tools/libraries/snippets
#4 Don't be a hero

The people at Brisbane Alt.Net are a very cluey bunch so it was great to get their input on the whole thing. They were a very attentive and helpful bunch. It was a massive buzz talking to them. I highly recommend it.

higgins presenter

...And Introducing Higgins

Naturally, I wrote the slides about writing on the bus, while on the bus. I don't have Powerpoint on my bus netbook, so I also had to write my own html-based slide presenter, which I've codenamed Higgins.

I've since added higgins to codeplex. You can get Higgins from codeplex and use it yourself, if you're so inclined.

 

Visit higgins at codeplex

 

Download higgins here

briz alt net sign kind of ironic i think

 

 

Test Driven Dentistry Is A Good Thing

I saw this quote in the footer of an email...

"Test-driven Dentistry (TDD!) - Not everything should be test driven"
- Michael Fogus

I'm no TDD-lover, not by a long shot, but I dismiss the claim that you shouldn't do test driven dentistry.

I think you probably *should* do test driven dentistry.

First write your test:

Setup:
   Put on lead coat.
   Perform X Ray.
   
Test:
   Assert: There are no unfilled cavities.

Teardown:
   Remove lead coat.

And then:

Run the test: it fails.

Fill the cavity.

Run the test: it passes.

The only special nuance of test-driven-dentistry is that you must resist the urge to refactor the mouth, as this would require re-executing your tests, which may cause unnecessary irradiation of the patient. Side effects are an encumbrance but not a show stopper.

 

The 'less crashy' release of NimbleText

I put out a minor update to NimbleText the other day and called it the 'less crashy' edition.

Here's the backstory.

I've received intermittent reports for a while that NimbleText would crash on startup. But I couldn't reproduce the problem, and even the people reporting the problem could rarely reproduce the problem.

After I put out a major update on Mar 17 I got this crash report from Scott Hanselman, via twitter:

@secretgeek I was going to tweet about it, but it crashes on startup for me.

Now this is vexing. I was on a bus at the time, with limited resources. I asked for a stack trace. (And I asked nicely, not like this guy)

But unfortunately:

@secretGeek can't...doesn't even start. Watson's before the UI shows up.

...which is even more vexing. (For the younger readers: to 'Watson' is to crash, and refers to Dr Watson. Internally at microsoft, this nomenclature is still used.)

So I asked for system specifications from the crashee, and learnt:

@secretGeek win64, ie9. Visit site, download, run, crash

Note that twitter is sufficient for most of the pertinent information, provided the crashee in fizzbin savvy. Non-fizzbin customers often have to give you the full backstory of their life and times before confirming that they are indeed using a Windows computer.

Well I haven't used IE9 myself, so that's the 'odd man out' for me. In fact as a chrome user, and occasional firefox user, I haven't tried running it from IE at all. When you write a windows application you don't think that testing various browsers should be a part of your regime.

But I had an immediate counter claim from OJ:

@secretGeek @shanselman I have Win7 x64, IE9, and it works fine for me. Download, run, all good. Leon, are you using 3rd party native dlls?

NimbleText is a very simple application. It has *no* dlls. It does use obfuscation though, and this is where my suspicions immediately shifted (wrongly as it turns out).

Once I was at a real computer again I tried downloading the app from IE and running it. It worked fine for me. Perhaps I could re-badge it with the 'works on my machine' certification and be done with it? No, these gloomy ruminations aren't helpful.

I tried again and again, and the trick soon announced itself.

When you click on an exe, what do you do next? Do you choose "run" or "save"? Personally I always choose "save". This became ingrained behavior in me many years ago. So it never occurred to me to press 'run'. But consider the psychology of the crashee: Scott Hanselman has personally installed every single piece of software on the entire internet. To allow him to perform this incredible feat he is going to be taking shortcuts anywhere he can. So instead of downloading the file and then running it, I tried just pressing "run". Instantly: the app would hang, then crash. Bingo.

Once you've got a repro, everything is straightforward.

The line that failed was an Assembly.Load:

var a = Assembly.Load(assemblyShortName);

Why does this fail when I run it from IE, but not when I download it and run it?

I assumed that some kind of security limitation didn't allow an Assembly Load in that scenario.

Rather than solve the problem of why the Assembly.Load failed, i looked at the code in question and found I could instead just use:

var a = Assembly.GetExecutingAssembly();

This code does *not* fail when the app is executed directly in IE. So I updated NimbleText and put out the new, less crashy, release.

But the mystery still wasn't solved to my satisfaction. I wanted to learn exactly why the Assembly.Load would fail. If it was a security exception, why didn't it look like one? I googled to no avail. I binged without success.

Next I built a small console app that tried many variations around the problem. I found I could get an Assembly.Load to succeed if I used the full name of the assembly, like this:

var a = Assembly.Load("NimbleText, Version=1.2.0.29995, Culture=neutral, PublicKeyToken=null");

What's the difference? I was expecting to need to use FUSLOGVW.exe the assembly binding log viewer, something I don't look on with any great excitement.

Then I noticed that when you run an app directly from IE it not only gets downloaded to temporary internet files, it also gets *renamed*. I tried this out locally and saw that renaming the file was the killer. Any old copy of NimbleText would crash if I renamed the executable.

The CLR assembly resolver expects that your assembly name is your assembly short name plus .dll or .exe. Hence, it's quite rude of IE to rename the file under you. So I blame IE. I'm not the first person to blame IE, and I won't be the last.

But I also blame myself, or rather my code. I'll no longer be using shortnames when trying to resolve an assembly. It's strong names or nothing as far as I'm concerned. As of now, I scoff at short names.

And, you should use NimbleText for all your text manipulating, data extracting, code generating needs. It boasts less crashiness.

 

Rethinking Toolbars in Visual Studio (or any IDE)

Count the Arrows on the Toolbar

Toolbars are kind of ridiculous. You start off with row after row of cryptic icons which mean almost nothing to you. Even after years of using a program you've probably only ever used half of them, and have no clear idea what the other half do, and probably don't need them anyway. Not to mention that it's so hard to come up with meaningful icons that most of them end up just being an arrow of some kind.

To (supposedly) overcome these kinds of problems Microsoft invented the 'ribbon,' but it hasn't made its way into Visual Studio yet. I think a different solution could be better:

Toolbars could work the same way as the taskbar does in Windows 7:

It would start out empty.

 

Clean. Beautiful. This is far preferable to today's approach: starting out full of clutter.

Thereafter, when you run a command (by selecting it from a menu option, for example), it would show up on the toolbar (in the same way that running an application in Windows 7 show up on the taskbar.

After that you would be able to right click it and choose to 'pin it' in place, so it stays.

There would need to be some difference in the behaviour, since commands and tasks don't work the same way as each other. Tasks are long running things, so an unpinned icon (in Windows 7) stays visible only so long as the task stays running. Commands are short running things, so if we followd the model exactly they would flicker onto the toolbar and then almost immediately dissappear before we have a chance to pin them. So instead the toolbar could show a 'rolling history' of (say) the ten or twenty most recently used commands. Or perhaps it would show all the commands used during this session. Or use a simple forumla to show commands that are used recently and or most often (similar to the way voting works at sites like reddit).

And you would be able to simply drag to rearrange the items on the toolbar, just like you can on the windows 7 taskbar.

I think this is better than a ribbon.

Some refinements of this idea are:

1. You might have a setting to say "Don't add a command to the toolbar if i've invoked it via a keyboard shortcut." Since you know the shortcut, you're unlikely to need the icon.

2. Some icons should only appear when you're editing certain types of files. The 'jumplist' for a given icon could let you quickly apply this kind of conditional showing/hiding.

3. Your toolbar settings could be exported, synchronised across computers (via dropbox) or shared with friends. They would be human readable.

4. It would be crucial to have an effective way to search for commands. This is true of the ribbon as well. (this part was well covered by Jeff Atwood in 2007).

See also: Rico Mariani discussing the future of Visual Studio (from 2008). He's no ribbon fan.

And a beta-tester of my blog tells me Hanselman has been covering similar territory in the last few days. That VS UI has just got to lighten up!

 

Where shall we have lunch?

All wise students of the Hitchhiker's Guide to the Galaxy will remember this quote:

The History of every major Galactic Civilization tends to pass through three distinct and recognizable phases, those of Survival, Inquiry and Sophistication, otherwise known as the How, Why and Where phases.

For instance, the first phase is characterized by the question How can we eat? the second by the question Why do we eat? and the third by the question Where shall we have lunch?

In accordance with that philosophy, I've studied the complex decision making usually required to reach group consensus on a lunch venue, and embodied the resulting deterministic algorithm in this simple web site:


https://secretgeek.net/lunch/


So any day you're wondering where I'm gonna be for lunch, just visit that site and it should be correct. (For the next two weeks.)