To 404 or to redirect, or REST should work both ways

When designing a RESTful interface, remember that while you're using nice & friendly URL semantics going in, your server app should apply the same concept to its response as well.

I've seen (and been guilty of) the following before:

if(!PermissionManager.UserCanViewOrder(_user, _order))
{
    Response.Redirect("~/");
}

A similar case is this one taken from one of the MonoRail guys' blog posts:

RoutingModuleEx.Engine.Add(
    PatternRule.Build("bycondition", "listings/<cond:new|old>", typeof(SearchController), "View"));
RoutingModuleEx.Engine.Add(
    PatternRule.Build("alllisting", "listings", typeof(SearchController), "Index"));
RoutingModuleEx.Engine.Add(
    PatternRule.Build("unmatch", "listings/*", typeof(SearchController), "RedirectToIndex"));

HINT: Look at the 3rd and final rule

Both these examples send a confusing message back to the client: they redirect them to some other page on invalid input.  In my mind this is conceptually similar to the following C# code:

try
{
      // Do something
}
catch
{
      return;
}

Yes, by performing redirects in these examples you're effectively performing one of the biggest software sins: swallowing errors

You see, all the client/user has of your system is your URL structure, and if something goes wrong it's up to you to let them know why it went wrong.  In HTTP terms this means sending back the correct HTTP status code that describes the status of their request.

In both of the above examples we're performing redirects, using Response.Redirect in the first example at least.  This sends a 302 status code to the client, and a 302 status code reads to the client as "Moved temporarily" or "Found" (but at a different URL).  This tells the client nothing about why their request couldn't be handled, just bumps them off to another page (probably without even an error message).

Look back at our two examples.  In case one the resource hasn't moved, the client just doesn't have permission to view that URL.  The correct status code to return in this case would be a 401 Unauthorized response.  In case two the resource isn't even there, so the correct status code to return would be our old friend 404 Not Found.

I don't blame you for not doing this before, I mean the REST page on Wikipedia only has this to say on the subject:

HTTP has a uniform interface for accessing resources, which consists of URIs, methods, status codes, headers, and content distinguished by MIME type.

It then goes on to talk about HTTP methods and REST, but status codes aren't mentioned at all (note to self, edit Wikipedia article on REST).

That sounds like a lot of work to implement, doesn't it?  I mean ASP.NET has the nice, friendly and convenient Response.Redirect method, but there's no Response.Unauthorized method, or Response.FileNotFound method.  Ignoring for the moment that those methods are a stupid idea and don't really belong on the Response object, there's actually a very simple way to send these codes to the client:

throw new HttpException(404, "File not found");

If you don't like the idea of throwing an exception then you can do the same thing manually:

Response.StatusCode = 404;
Response.Status = "404 File not Found";
Response.End();
 

 

See, not that hard is it?

Fixing TreeView control rendering

While I'm on the subject, I thought I would go over another more annoying bug with the .NET framework which affects the TreeView control and (X)HTML compatibility.

The jist of the problem is that when using the TreeView control with client-side script it has a tenancy to render a <script> tag without the required "type" attribute, very annoying seeing as Microsoft likes to sing the praises of the new standards-compliant control rendering in .NET 2.0.

As a fix you can use the same Render method override trick as before, adding in a simple Replace for these non-compliant script tags:

protected override void Render(HtmlTextWriter writer) { string renderedHtml = String.Empty; using (System.IO.StringWriter sw = new StringWriter()) { HtmlTextWriter newWriter = new HtmlTextWriter(sw); // Render "MyBase.Render()" to a String instead of the writer base.Render(newWriter); newWriter.Flush(); renderedHtml = sw.ToString(); newWriter.Close(); } // Fix broken (incompatible) <script></script> blocks, such as the one emitted by the TreeView control renderedHtml = renderedHtml.Replace("<script>", "<script type="\">"); // Write the modified HTML String to the Response's writer writer.Write(renderedHtml); }


This also has the added bonus of fixing any other broken script blocks in the currently rendered page
, which is nice..

Ubuntu Woes

Well, I recently decided to splash out on a new box. Hardware specs aside I thought as an experiment I would go the Linux route with this one - you know, install Ubuntu, MythTV and maybe try some Mono development on it.

Now, I hear that the Linux desktop is approaching maturity, but I'm not sure I see it. After running, or attempting to run, Ubuntu for several weeks I thought I would post my thoughts (both positive and negative)..
  1. PRO: Installation was relatively easy, I like the idea of the installation CD booting you into a live desktop before you start the installation process very handy especially for recovery.
  2. CON: Installation was buggy. At one point you choose (it appears) which drives you would like available in the final install and where to mount them. It then proceeds to format the aforementioned drives that you have been filling with data for the last 4 years into NTFS and before you notice, oh well..
  3. PRO: Once started, installation was very straightforward and quick, equaling Windows in it's simplicity.
  4. CON: First restart after installing I was greeted with a flashing cursor on a black screen. Turns out Grub didn't like my 4-port SATA controller. Luckily my new motherboard has enough free SATA ports that I could cut the controller out of the equation (for now).
  5. CON: The default ATI drivers are pretty shoddy, and after upgrading to what I though would be better performance drivers it totally broke all video playback through banshee, xine, mythtv etc. I'd play a movie and get unceremoniously dumped back to the login screen (the lack of blue screen would be, I suppose, a PRO though)
  6. PRO: NTFS support is coming on in leaps and bounds, the NTFS-3g project is testament to this and runs excellently.
  7. CON: As part of the hardware upgrade I had decided to copy the data from my old RAID array (A Highpoint RocketRAID 133) to one of my shiny new SATA drives. Unfortunately the DIY Linux drivers on the highpoint site don't seem to like 64-bit OSes - I could mount the drive and see the files on it, but on trying to copy the files the process would hang indefinitely with little to no sign of progress. After giving it some space (left it for 2 days with no noticeable change, the rest of the system was stable) I gave in and decided to find another machine to grab my data.
  8. PRO: I'm liking the Synaptic package manager and general apt-get commands, very useful.
  9. CON: You're pretty much tied to the packages the community have added to Synaptic. If you want something that people haven't thought to add, or you want/need the latest version of something existing (I'm a bit of a cutting-edge whore as you will find out), then you pretty much need to compile from source (along with the other dependencies of the program). Whatever was wrong with installer programs, a-la Windows? Why should I need to "./configure", "make", "make install" every time I want to install something? If it's that easy then why don't they lump it all into one command?
  10. CON: Something needs to be done about DVD playback. Once you get through all the legal issues and finally get libcss or whatever installed then it's still buggy - jumping video and pixilated picture even on my shiny new Core 2 Duo box.
Well I'm sure there's more, for the time being I've installed Vista RC1 (not without it's issues but that's another post entirely). I still need to get back into Linux to sort a couple of things (foolishly re-formatted a drive in xfs or whatever), which means re-enabling grub after Windows overwrote the MBR, Ho Hum..

Moving ViewState to the Bottom of the page

I was researching some weird ViewState bugs today and stumbled across this post on moving the ViewState to the bottom of the page. The main point of this is SEO; I seem to recall that Google (and possibly other search engines) only read the first xkb of a page, so when a page has a fair few kb of ViewState on it this can screw up your SEO and mean information you want indexing doesn't.. (get..)

Enter Scott's method. It's pretty straightforward really, you're just physically moving the field to the just before the end of the form tag. The one problem I have with his method is that with .NET 2.0 in XHTML compatibility mode the ViewState field is wrapped in a <div> tag as you're not allowed <input> elements in the root of a form tag. Scott's method breaks this convenient addition by moving the __VIEWSTATE input field out of the div, what we ideally want is the div moving too, hence I came up with a new version of the Regular Expression he uses:

(<div>[^<]*<input.*name="__viewstate"[^>]*>[^<]*</div>)


All pretty straightforward, you may notice that I chose to strip out the type="hidden" part of the original Regex so as to make things a bit more more generic. I haven't delved into the (faster) non-Regex method Scott outlines as I personally think the Regex method is more readable - feel free to give it a try if you like.

A better way to fix the TreeView's invalid <script> attribute

I was poking about in Reflector today and think I found a better way to fix the invalid <script> attribute that the TreeView control insists on putting out when the EnableClientScript attribute is set to "true".

The root of the problem seems to be a static private field in the TreeView control called "populateNodeScript" which contains the hard-coded (!) <script> tag - my solution uses reflection to fix this string so that when the TreeView uses this value it uses the fixed value rather than the default.

This is the code that needs to be placed in the OnInit handler of your page (I use OnInit but anywhere before OnPreRender should do the trick):

private static void FixTreeViewScriptNode(){ FieldInfo field = typeof(TreeView).GetField("populateNodeScript", BindingFlags.NonPublic | BindingFlags.Static); string populateNodeScript = Convert.ToString(field.GetValue(null)); field.SetValue(null, populateNodeScript.Replace("<script>", "<script type="\"text/javascript\">")); }

Note: I'm not sure of the performance impact of this script as I haven't had much of a chance to performance test it yet - I expect the reflection penalty will have less of a hit (memory-wise anyway) than the render-to-string page override method I have detailed before. There is always scope for improvement - as we're setting a static field then we should only need to set this once, though I'm unsure when or where (global.asax?)