{STATIC} hippo

...on becoming a great developer...
posts - 38, comments - 12, trackbacks - 0

My Links

Archives

Post Categories

ASP.NET MVC Captcha

 

I was looking for an ASP.NET MVC Captcha control and stumbled upon Nick Berardi's (http://www.coderjournal.com/2008/06/mvc-captcha-for-preview-release-3/).

First of all I'm using IIS7 and had trouble getting the HttpHandler to register properly.  I ended up with the following steps to get it to work:

  1. Add <add verb="GET" path="captcha.ashx" validate="false" type="ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha" /> to <httpHandlers> in Web.config
  2. Add <add name="CaptchaImageHandler" verb="GET" path="captcha.ashx" type="ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha" /> to the <handlers> section within <system.webserver> in Web.config
  3. Add routes.IgnoreRoute("{handler}.ashx"); to Global.asax

So that got it working, but there were a few pieces of functionality I felt were missing:

1) I wanted the ability to style the CaptchaTextBox

The site I'm working with is pretty design intensive and I needed a way to inject CSS information into the CaptchaTextBox Html Helper.  I reworked the CaptchaHelper and modified the CaptchaTextBox as well as added some overloads:

   1: public static string CaptchaTextBox(this HtmlHelper helper, string name)         
   2: {             
   3:     return helper.CaptchaTextBox(name, null);         
   4: }
   5: public static string CaptchaTextBox(this HtmlHelper helper, string name, 
   6:     Object htmlAttributes)
   7: {
   8:     return helper.CaptchaTextBox(name, 
   9:         ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));         
  10: }         
  11: public static string CaptchaTextBox(this HtmlHelper helper, string name,
  12:  IDictionary<String, Object> htmlAttributes)         
  13: {             
  14:     ModelState state;             
  15:     TagBuilder builder = new TagBuilder("input");             
  16:     builder.MergeAttributes<string, object>(htmlAttributes);             
  17:     builder.MergeAttribute("type", "text");             
  18:     builder.MergeAttribute("name", name);             
  19:     builder.MergeAttribute("id", name);             
  20:     builder.MergeAttribute("value", "");             
  21:     builder.MergeAttribute("maxlength", 
  22:         ManagedFusion.Web.Controls.CaptchaImage.TextLength.ToString());             
  23:     builder.MergeAttribute("autocomplete", "off");             
  24:     
  25:     if (helper.ViewData.ModelState.TryGetValue(name, out state) 
  26:         && (state.Errors.Count > 0))             
  27:     {                
  28:         builder.AddCssClass("input-validation-error");             
  29:     }             
  30:     return builder.ToString(TagRenderMode.SelfClosing);         
  31: }

I got the TagBuilder idea from checking out the Reflector on System.Web.MVC.  Pretty cool stuff there.  So now I can use the CaptchaTextBox like so:

<%= Html.CaptchaTextBox("captcha", new { @class = "field" })%>

2) I wanted the CaptchaValidationAttribute to invalidate my Model if the captcha isn't valid

(instead of inject a captchaValid with a value of false into my routedata which is what it does off the shelf)

For this I modified the CaptchaValidationAttribute class.  The first thing I did was make add an ErrorMessage string property.  Then I modified the OnActionExecutingContext method to look like this:

   1: public override void OnActionExecuting(ActionExecutingContext filterContext)         
   2: {             
   3:     // get the guid from the post back             
   4:     string guid = filterContext.HttpContext.Request.Form["captcha-guid"];             
   5:     // check for the guid because it is required from the 
   6: //rest of the opperation             
   7:     if (String.IsNullOrEmpty(guid))            
   8:     {                 
   9:         filterContext.Controller.ViewData.ModelState
  10:             .AddModelError(Field, ErrorMessage);                 
  11:         return;             
  12:     }            
  13:     // get values             
  14:     CaptchaImage image = CaptchaImage.GetCachedCaptcha(guid);             
  15:     string actualValue = filterContext.HttpContext.Request.Form[Field];             
  16:     string expectedValue = image == null ? String.Empty : image.Text;            
  17:     // removes the captch from cache so it cannot be used again             
  18:     filterContext.HttpContext.Cache.Remove(guid);            
  19:     // validate the captch             
  20:     if (String.IsNullOrEmpty(actualValue) 
  21:         || String.IsNullOrEmpty(expectedValue) 
  22:         || !String.Equals(actualValue, expectedValue, 
  23:                 StringComparison.OrdinalIgnoreCase))             
  24:     {                 
  25:         filterContext.Controller.ViewData.ModelState
  26:             .AddModelError(Field, ErrorMessage);                 
  27:         return;             
  28:     }         
  29: }


Now I can use the CaptchaValidationAttribute like this:

 

   1: [CaptchaValidationAttribute()]
   2: public ActionResult Register(FormCollection form)         
   3: {
   4:     // INCREDIBLY OVER-SIMPLIFIED BUT YOU GET THE IDEA             
   5:     if (!ViewData.ModelState.IsValid)             
   6:     {             
   7:     return View();
   8:     }         
   9: }

Print | posted on Tuesday, October 28, 2008 5:53 PM | Filed Under [ ASP.NET ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 7 and 2 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET