Sunday, October 30, 2011

Overriding FormsAuthentication for some URLs

Is this for you?

How many times do you need you're website to have both public and private pages?

How many times did you thought that creating virtual directories with specific web.config files was lame?

If you feel the pain and want it to go away, read on!
Also note that although I'll refer a lot to HttpHandlers on this post, everything here (except the route registration) is also true for common web pages.

Be sure to have a look at this

A few days ago I wrote about handling HttpHandlers with ASP.net routing.
I'll refer to those extension methods to register my test handler route, so have a look on that post before continuing.

Now what I need is a way to override the default FormsAuthentication configuration for a specific set of HttpHandlers.

Virtual Folder, web.config and the ASHX files

FormsAuthentication support this out-of-the-box by simply putting the resources with special security concerns on a separated folder with its own web.config file.

So if you want a virtual directory to allow access to anonymous users just add a web.config file with nothing but this in it:


 
  
   
  
 

This will work for any resource can can be accessed through an URL but this isn't always the case with HttpHandlers.

FormsAuthentication and HttpHandlers without ASHX file

Using the extension methods I wrote on the said previous post you can create an HttpHandler by simply creating a new class and implementing the IHttpHandler interface and point a route to it, just like this:
RouteTable.Routes.MapHttpHandlerRoute("Test", "Unsecured/Controllers/Test", new MyApplication.UnsecuredHandlers.MyUnsecuredHandler());
This means that, whenever you call ht**://mydomain/Unsecured/Controllers/Test the request will be routed to MyUnsecuredHandler instance, not to a physical uri location as usual.
Now have a look at the route. It begins with Unsecured right? Keep reading and you'll understand why.

But we're not there yet, what I really want is to say that some of my handlers allow anonymous requests, and for that I'll edit my website web.config and add the following:


 
  
   
    
   
  
   

Now its done!
Notice that on the location path I only have unsecured. This will grant request permissions to all routes that begin with unsecured!
This is great because now I don't have to bother about structuring the resources on virtual directories and possibly duplicating the code for different scenarios.
Whenever I need a Page or and HttpHandler to be available to anonymous users I just need to create a route to it that begins with unsecured.

If you don't like this approach (specially for pages where the url is visible for the users) you can always add as much location entries on the web.config as you like.

If you're not using Routing you can still specify a location to your resources putting the uri of the Page or HttpHandler on the path attribute:


 
  
   
    
   
  
   

Saturday, October 22, 2011

PubSub with JQuery (Events)

What is PubSub?

PubSub stands short for Publish/Subscriber and is a technique based on events that lets you decouple the logic of an application.
The basic concept is to have something that triggers an event (the publisher) and have one or more listeners (the subscribers) that are waiting for that event to be fired to act appon.

When to use this?

The short answer is: UI Decoupling. Sometimes sections of a webpage must communicate with each other, react on each other activities. To do this we usually have to make the sections aware of each other, making it much harder to maintain.
Using Pub/Subs we just publish the events and forget about it, "someone" else on the page may make some use out of it.

How does it work?

The concept is pretty easy to understand and the implementation follows the simplicity.

The Publisher

This guy here usually reacts on an action and screams out loud:

"I got something here, it's called [myevent] and comes along with this extra information.
Anyone interested?"

$(document).trigger('myevent', [age]);

The Subscriber

This guy meaning of life is to wait for someone says he got a [myevent].
There can be as many guys as you want waiting for the same event, and each can react differently.
 $(document).bind('myevent', function(e, age){ });

Sample Code

Bellow you find a piece of code that shows the functionality in action on a very simple scenario.
Basically you have a textbox where you can enter numbers, meaning ages for the case.
Every time you click on the publish button, an event is triggered, publishing a message that contains the typed age.
Listening are two subscribers that want to be notified whenever that event is triggered.
One of the subscribers only cares about ages bellow 18, the other one cares about everything else.

Create a new html file and paste this code there and show it on a browser. You'll need internet connection as I'm using Google's CDN to get JQuery.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
 <title>JQuery PubSub Demo</title>
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
 
 <style type="text/css">
  .Subscriber{ display:block; list-style-type: none; float: left; width: 200px; border: solid 1px #dcdcdc; margin: 5px; padding: 5px; }
 </style>
</head>
<body>
<h2>Publisher</h2>
<p>
 Publish: 
 <input id="txtPublisherAge" type="text" />
 <input type="button" value="publish" onclick='Publish($("#txtPublisherAge").val())' />
</p>

<h2>Subscriber</h2>
<ul id="ulLessThan18" class="Subscriber"></ul>
<ul id="ul18Plus" class="Subscriber"></ul>

<script type="text/javascript">

 $(document).ready(function(){
  
  // handle ages < 18
  $(document).bind('myevent', 
   function(e, age){
    if(age < 18) {
     $('#ulLessThan18').append('<li>Handled age: ' + age + '</li>'); 
    }
   });
   
  // handle ages >= 18
  $(document).bind('myevent', 
   function(e, age){
    if(age >= 18) {
     $('#ul18Plus').append('<li>Handled age: ' + age + '</li>'); 
    }
   });
   
 });

 // Everytime this method is called, the 'myevent' event is published with the passed age value
 function Publish(age){
  $(document).trigger('myevent', [age]);
 }

</script>

</body>
</html>

Cheers

Friday, October 21, 2011

ASP.net 4.0 HttpHandler Routing Support

I'm a big fan of ASP.net Routing and I use it a lot on my ASP.net Webforms applications.

If you're just getting started with Routing on ASP.net Webforms I recommend reading this post from Scott Guthrie.

Ok now, back to the subject, one thing about Routing is that it doesn't support routes that point to HttpHandlers (common .ASHX files).

The code bellow comes just to overcome this limitation by adding a new method (and one overload) to the RoutesCollection object that will let you map a route to an *.ashx url or directly to the handler object.

Edit (2011/11/03): I just updated this due some problems when reusing the same handler for different requests. Now each request gets its own handler like it should.

The Code

namespace System.Web.Routing
{
 public class HttpHandlerRoute<T> : IRouteHandler where T: IHttpHandler
 {
  private String _virtualPath = null;

  public HttpHandlerRoute(String virtualPath)
  {
   _virtualPath = virtualPath;
  }

  public HttpHandlerRoute() { }

  public IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
   return Activator.CreateInstance<T>();
  }
 }

 public class HttpHandlerRoute : IRouteHandler
 {
  private String _virtualPath = null;

  public HttpHandlerRoute(String virtualPath)
  {
   _virtualPath = virtualPath;
  }

  public IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
   if (!string.IsNullOrEmpty(_virtualPath))
   {
    return (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler));
   }
   else
   {
    throw new InvalidOperationException("HttpHandlerRoute threw an error because the virtual path to the HttpHandler is null or empty.");
   }
  }
 }

 public static class RoutingExtension
 {
  public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
  {
   var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile));
   routes.Add(routeName, route);
  }

  public static void MapHttpHandlerRoute<T>(this RouteCollection routes, string routeName, string routeUrl, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null) where T : IHttpHandler
  {
   var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute<T>());
   routes.Add(routeName, route);
  }
 }
}

How to use it

// using the handler url
routes.MapHttpHandlerRoute("DoSomething", "Handlers/DoSomething", "~/DoSomething.ashx");

// using the type of the handler
routes.MapHttpHandlerRoute<MyHttpHanler>("DoSomething", "Handlers/DoSomething");

And that's it! :)