2005 2006 2007 2008 2009 2010 2011 2015 2016 2017 aspnet azure csharp debugging elasticsearch exceptions firefox javascriptajax linux llblgen mongodb powershell projects python security services silverlight training videos wcf wpf xag xhtmlcss

Simplified Universal HttpHandlerFactory Technique

A few months ago I wrote about the Universal HttpHandlerFactory Technique where you have one HttpHandlerFactory that all your ASP.NET processing goes through and then in that HttpHandlerFactory you then choose what HttpHandler is returned based upon what directory or file is accessed.  I still like this approach for certain scenarios, but my blog got to the point where I was managing 11 different HttpHandlers for about 25 different path and file patterns.  So, it was time to simplify.

What I came up with was basically my go to card for everything: put it in SQL Server.  From there I just checked the URL being accessed against the patterns in a database table and then looked up what HttpHandler to use for that particular request.  Then, of course, I cached the HttpHandler for future file access.

Here are my SQL Server tables (yes, I do all my design in T-SQL-- SQL GUI tricks are for kids):

create table dbo.HttpHandlerMatchType  (
HttpHandlerMatchTypeId int primary key identity not null,
HttpHandlerMatchTypeName varchar(200) not null
) 

insert HttpHandlerMatchType select 'Contains'
insert HttpHandlerMatchType select 'Starts With'
insert HttpHandlerMatchType select 'Ends With'
insert HttpHandlerMatchType select 'Default'

create table dbo.HttpHandler (
HttpHandlerId int primary key identity not null,
HttpHandlerMatchTypeId int foreign key references HttpHandlerMatchType(HttpHandlerMatchTypeId),
HttpHandlerName varchar(300),
HttpHandlerMatchText varchar(200)
) 

Here is the data in the HttpHandler table:

 HttpHandler Table

Looking at this image and the SQL Server code, you can see that I'm matching the URL in different ways.  Sometimes I want to use a certain HttpHandler if the URL simply contains the text in the HttpHandlerMatchText and other times I'll want to see if it the URL ends with it.  I included an option for "starts with" as well, which I may use in the future.  This will allow me to have better control of how paths and files are processed.  Also, notice that one is "base".  This is a special one that basically means that the following HttpHandler will be used (keep in mind we are in a class that inherits from the PageHandlerFactory class-- please see my original blog entry):

base.GetHandler(context, requestType, url, pathTranslated);

Now in my HttpHandlerFactory's GetHandler method I'm doing something like this (also note how LLBLGen Pro helps me simply my database access):

HttpHandlerCollection hc = new HttpHandlerCollection( );
hc.GetMulti(new PredicateExpression(HttpHandlerFields.HttpHandlerMatchTypeId != 4));

IHttpHandler hh = null;
foreach (HttpHandlerEntity h in hc) {
    hh = MatchHttpHandler(absoluteUrl, h.HttpHandlerName.ToLower( ), h.HttpHandlerMatchTypeId, h.HttpHandlerMatchText.ToLower( ));
    if (hh != null) {
        break;
    }
}

This is basically just going to look through all the HttpHandlers in the table which are not the "default" handler (which will be used when there is no match).  The MatchHttpHandler method basically just passes the buck to another method depending of whether I'm matching the URL based on Contains, StartsWith, or EndsWith.

private IHttpHandler MatchHttpHandler(String url, String name, Int32 typeId, String text) {
    IHttpHandler h = null;
    switch (typeId) {
        case 1:
            h = MatchContains(url, name, text);
            break;

        case 2:
            h = MatchStartsWith(url, name, text);
            break;

        case 3:
            h = MatchEndsWith(url, name, text);
            break;

        default:
            throw new ArgumentOutOfRangeException("Invalid HttpHandlerTypeId");
    }

    return h;
}

Here is an example of one of these methods; the others are similiar:

private IHttpHandler MatchContains(String url, String name, String text) {
    if (url.Contains(text)) {
        return GetHttpHandler(name);
    }
    return null;
}

As you can see, it's nothing fancy.  The last method in the chain is the GetHttpHandler, which is basically a factory method that converts text into an HttpHandler object:

private IHttpHandler GetHttpHandler(String text) {
    switch (text) {
        case "base":
            return new MinimaBaseHttpHandler( );

        case "defaulthttphandler":
            return new DefaultHttpHandler( );

        case "minimaapihttphandler":
            return new MinimaApiHttpHandler( );

        case "minimafeedhttphandler":
            return new MinimaFeedHttpHandler( );

        case "minimafileprocessorhttphandler":
            return new MinimaFileProcessorHttpHandler( );

        case "minimapingbackhttphandler":
            return new MinimaPingbackHttpHandler( );

        case "minimasitemaphttphandler":
            return new MinimaSiteMapHttpHandler( );

        case "minimatrackbackhttphandler":
            return new MinimaTrackBackHttpHandler( );

        case "minimaurlprocessinghttphandler":
            return new MinimaUrlProcessingHttpHandler( );

        case "projectsyntaxhighlighterhttphandler":
            return new ProjectSyntaxHighlighterHttpHandler( );

        case "xmlrpcapi":
            return new XmlRpcApi( );

        default:
            throw new ArgumentOutOfRangeException("Unknown HttpHandler in HttpHandlerMatchText");
    }
}

There is one thing in this that stands out:

case "base":
    return new MinimaBaseHttpHandler( );

If the "base" is simply a call to base.GetHandler, then why am I doing this?  Honestly, I just didn't want to pass around all the required parameters for that method call.  So, to make things a bit more elegant I created a blank HttpHandler called MinimaBaseHttpHandler that did absolutely nothing.  After the original iteration through the HttpHandlerCollection is finished, I then do the following (it's just a trick to the logic more consistent):

if (hh is MinimaBaseHttpHandler) {
    return base.GetHandler(context, requestType, url, pathTranslated);
}
else if(hh != null){
    if (!handlerCache.ContainsKey(absoluteUrl)) {
        handlerCache.Add(absoluteUrl, hh);
    }
    return hh;
}

One thing I would like to mention is something that that sample alludes to: I'm not constantly having everything run through this process, but I am caching the URL to HttpHandler mappings.  To accomplish this, I simply setup a simple cached dictionary to map URLs to their appropriate HttpHandlers:

static Dictionary<String, IHttpHandler> handlerCache = new Dictionary<String, IHttpHandler>( );

Before ANY of the above happens, I check to see if the URL to HttpHandler mapping exists and if it does, then return it:

if (handlerCache.ContainsKey(absoluteUrl)) {
    return handlerCache[absoluteUrl];
}

This way, URLs can be processed without having to touch the database (of course ASP.NET caching helps with performance as well).

Related Links

All my work in this area has been rolled-up into my Themelia ASP.NET Framework, which is freely available on CodePlex. See that for another example of this technique.

Seriously Awesome Blog

No... unfortunately not this one.  Today I was across Rick Strahl's blog.  I've been reading his stuff for a long time now and have been a big fan of his work for a while, but some for some reason it was only today that I got around to thinking "wait... maybe he has a blog!".  In any case, if you read my blog, then you REALLY need to be reading his.  His work covers very similar topics: .NET, ASP.NET Internals, JavaScript, WCF Stuff, COM, Windows Live Writer API, but with the exception that his, of course, is much better.  Aside from his blog, his articles and papers are also incredible.  One in particular I would like to point out is a classic: A low-level Look at the ASP.NET Architecture, an incredibly in depth look at the internals of ASP.NET (and required reading for all ASP.NET developers!).  You can also see him answering questions all over the forums (places where you won't see me!  Scary places!)

He's definitely on my list of "people smart than me" (there's a success principle that states that the key to success to to surround yourself with people smarter than yourself).  Also on that list would be Scott Hanselman, Fritz Onion, Charles Petzold, Don Box, Frans Bouma, and Brad Abrams (there are more, but as a minimalist I feel weird when a list gets too long)  These are my .NET role models and you if don't have their blogs in your feed reader, then you seriously need to get on that one.  If you have too many already, delete mine.  BTW, they each have something in common: they are either MVPs or work at Microsoft (or are kind of both):

Related Links

HnD Customer Support and Forum System

If you are curious about LLBLGen Pro or have been using it for a while and want to further more skills with it, then you simply must check out Frans Bouma's HnD (Help and Discuss), an ASP.NET-based Customer Support and Forum System.  It was actually released back in December 2006, but I only just now got around to checking it out and I have to say it's really, really nice.  But what else would you expect from the person who created the world's most powerful database abstraction system (LLBLGen Pro)?

You can actually see an example of HnD by going to LLBLGen Pro's support forum.  I've been using their support forum for a while now and I could seriously tell a difference in usability from their own system.  One feature of HnD I definitely want to mention is something that many forum's don't have, but all need: when replying, it's critical to see the message you are replying to as you are typing (this is one of the reasons I switch to Gmail from Yahoo!)  Frans thought of this and HnD has that ability.  HnD even allows for attachments and has an attachment approval system for moderators, which is really nice.  The feature list goes on and on.

Not only is the end product nice and easy to use, it's released with full source code (just as LLBLGen Pro is buy, when you buy it).  However, unlike the commercial product LLBLGen Pro, HnD is released under the GPLv2 license, so... we can have all kinds of fun messing with it.  From my perspective, this is one of the greatest things about it and is exactly why I released Minima (a minimalistic ASP.NET 2.0 blog engine built using LLBLGen Pro).  Simple, to the point, source is provided, and the source is actually easy to navigate.

The solution is split into an SD.HnD.Utility project which contains very base level functionality (much like Minima's General project), it includes an SD.HnD.DAL project which contains the LLBLGen Pro DAL (much like Minima's MinimaDAL project), it includes an SD.HnD.BL project which contains "business logic" for Hnd (much like Minima's MinimaLibrary project), and finally it includes the web site.

This is an incredible project for anyone who wants to strengthen their LLBLGen Pro skills.  I can tell you that it has personally already helped me with my own LLBLGen Pro skills.  So, whether you want a really nice ASP.NET-based forum system, want to learn more about ASP.NET data binding, want to learn LLBLGen Pro for the first time, or just want to enhance your LLBLGen Pro skills, you should seriously considering nabbing the source code for HnD.

As a postscript, if you are unfamiliar with Frans Bouma's work, then should check out his blog at the below link.  His work is always great and he definitely deserves his MVP many times over.

Related Links

Windows Live Writer RULES

Microsoft Windows Live Writer (Beta 2) is by far and away one of the coolest tools I've used in a long time.  Since I created Minima, I was using my own extremely lame WPF app to do all my posting and it made posting a bore.  I've been meaning to put some time into making a more interesting WPF app, but instead Windows Live Writer saved the day.  With this thing I can post new entries, save drafts, set labels, as well as view and edit previous entries.

 Having said all that, setting it up wasn't that easy.  Well, the setup was simple, but figuring out what to setup wasn't.  I kept thinking that there was some .NET interface you had to implement, because the documentation kept talking about it's API and gave COM and .NET examples.  Well as it turns out, all you have to do is implement a well known blogging API and point WLW to it!  In my case, I chose the Metaweblog API.

Setting this API was actually rather simple, though it took some experimentation at first as I've never worked with the API at first.  Also, this API uses XML-RPC calls and at first and, at first, I figured I would have to write the XML listener and all XML messages manually.  It turns out that there's a nice API called XML-RPC.NET.  You set this up similar to how you setup a WCF service: via interfaces.

Here's the basic idea behind the XML-RPC.NET API:

[XmlRpcService(Name = "Minima API", AutoDocumentation = true)]
[XmlRpcUrl("http://www.netfxharmonics.com/xml-rpc/")]

public class XmlRpcApi : XmlRpcService
{
    [XmlRpcMethod("blogger.getUsersBlogs")]
    public BlogInfo[] GetUsersBlogs(String key, String username, String password) {
        // Stuff goes here
    }
}

You just set two class-level attributes and then set a method-level on each method.  Then you expose this class as an HttpHandler as the XmlRpcService class this class is inheriting from actually implements the IHttpHandler interface, which is rather convenient.

How did I know what methods I had to implement?  Well, the Metaweblog API "specification" is NOT a real specification, it's just an article that only mentions parts of it.  Also, XML-RPC.NET doesn't seem to have any useful tracing abilities, so that was out.  After a while though, I just found someone else's web site that implements the Metaweblog API and looked their API documentation (you can just look at the sample API below).  It turns out that to use the Metaweblog API means you will be using parts of the Blogger API as well.  Interesting...

Being a minimalist though, I wasn't about to implement ALL functionality.  So I setup an ASPX page that took the Request.InputStream, pointed WLW at the page, and when WLW did a request I got an e-mail from my ASPX page.  When I saw that WLW was calling a specific function, I implemented that specific one.  Of course I also had to implement specific data structures as well.  Really though, all you have to do is use XML-RPC.NET to implement the functions it wants and give it the structures in the Metaweblog API (as you can see in the sample API below) and you're done.

[As a side note, if you aren't familiar with what I mean by accessing the Request.InputStream steam, this stream contains the information that comes to the ASPX page in the POST portion of the HTTP request.  You will often access this when you are creating manual XML services (see my XmlHttp Interop article below for an example).  Here is an example of getting the input stream:

Byte[] buffer = new Byte[context.Request.InputStream.Length];
context.Request.InputStream.Read(buffer, 0, (Int32)context.Request.InputStream.Length);
String postData = ASCIIEncoding.UTF8.GetString(buffer);

You could use something like this to view what information is being sent from WLW.]

In my debugging I found that WLW has a tremendous number of extremely weird bugs.  For example, one of the structures I needed to implement was a structure called "Post" (I'm using the term structure, but it's just XML over the wire and it's a class in my API-- not a struct).  However, WLW would give me errors if some of the fields were null and would give me a different error if they weren't null, but even then, it was only one some functions.  So I had to create two versions of "Post".  One called "Post" which only had a few members, and the other called "FullPost", which had everything.  Strange.  Oh well... I've seen worst (ever use Internet Explorer?)

In the end though, WLW was talking seamlessly with my API.  I was really, really dreading making a better blog client as that felt like such a waste of time (and there was NO way I was going to use a web client-- WPF RULES!). Windows Live Writer (Beta 2) has already been a great help for me in the past week. Not just WLW itself though, but also some of the great plugins you can use with it. For example, in this write-up, I used a Visual Studio pasting plugin to allow me to copy from VS2005 and paste here to get fancy color syntax. Cool!

Related Links

Extremely Misunderstood Software

Today I was thinking about some of the misunderstandings going around about software. Actually my life revolves around misunderstandings in every area of my life (not just my life in technology!) and I'm usually thinking about it in some respect. I can recall back to early 2004 when I was the black sheep in both the Microsoft AND Mozilla communities for promoting .NET *and* web standards. The Mozilla-ish (open-source) guys would mock the severe inefficiencies and painful security flaws of .NET (though they never ACTUALLY used .NET!) and the Microsoft-ish (.NET) guys would mock call me personally unrepeatable names for even suggesting that web developers should learn the foundational principles of JavaScript, CSS, and XHTML before they starting asking a load of "how do I..." questions. Now while the open-source community has wised up a bit, I still get extremely vile insults from the git-r-done areas .NET community where even mentioning proper development or training will get you killed. In any case, this is my life and like I say... not just in technology. Basically, everyone hates me :)

So, here's my concise (yeah like I'm capable of that) list of software that is EXTREMELY misunderstood. I'm not going to go into any detail except put a single line by the item which should give you a hint as to what the software really is. This is important: if you understand one of the confusions, that confusion is analogous to each of the others. For example, if you know how insane it is to compare SQL Server 2005 to MySQL then, guess what... you now understand why it's insane to compare Firefox to IE or LLBLGen Pro to .netTiers or NHibernate. You just can't do it! Yet, people all day long give me unjustifiable grief about this stuff and I am forced to spend literally 70% of my time in marketing instead of programming!

I'll shut up now... here's the list:

List of Extremely Misunderstood Software