Ejemplo de código BotDetect ASP.NET MVC CAPTCHA VB.NET

Este ejemplo de BotDetect ASP.NET MVC muestra como utilizar BotDetect ASP.NET CAPTCHA para proteger aplicaciones ASP.NET MVC. Partiendo por el proyecto estándar de ASP.NET MVC creado desde Visual Studio (File > New Project > Visual VB.NET > Web > ASP.NET MVC Web Application), modificaremos la página Register.aspx para incluir nuestro CAPTCHA, asegurando que sólo sean humanos los que puedan crear cuentas de usuario.

Cómo el proyecto de inicio de ASP.NET MVC contiene mucho código, no incluiremos todo el mismo en esta explicación, sólo las partes que debemos modificar para lograr la inclusión de nuestro BotDetect CAPTCHA a la página de registro. Considere que este ejemplo requiere la BotDetect ASP.NET CAPTCHA en su versión 2.0.13 o superior.

Ubicación de los archivos del proyecto

Por defecto, los archivos de este ejemplo se encuentran en:
C:\Program Files\Lanapsoft\BotDetect\ASP.NET 2.0\v2.0\Samples\VBNetBotDetect2MvcDemo\.

También puede acceder a este ejemplo desde el menú inicio:
Programs > Lanapsoft > BotDetect > ASP.NET 2.0 > v2.0 > Samples > VB.Net BotDetect ASP.NET MVC CAPTCHA Sample.

Views/Account/Register.aspx

Código fuente completo

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" 
  Inherits="System.Web.Mvc.ViewPage" %>

<%@ Import Namespace="VbNetBotDetectMvcDemo" %> 

<asp:Content ID="registerHead" ContentPlaceHolderID="head" 
  runat="server">
  <title>Register</title>
</asp:Content>

<asp:Content ID="registerContent" ContentPlaceHolderID="MainContent" 
  runat="server">
  <h2>Account Creation</h2>
  <p>
    Use the form below to create a new account. 
  </p>
  <p>
    Passwords are required to be a minimum of <%=Html.Encode( _ 
      ViewData("PasswordLength"))%> characters in length.
  </p>
  <%= Html.ValidationSummary() %>
  
  <% Using Html.BeginForm() %>
    <div>
      <fieldset>
        <legend>Account Information</legend>
        <p>
          <label for="username">Username:</label>
          <%= Html.TextBox("username") %>
          <%= Html.ValidationMessage("username") %>
        </p>
        <p>
          <label for="email">Email:</label>
          <%= Html.TextBox("email") %>
          <%= Html.ValidationMessage("email") %>
        </p>
        <p>
          <label for="password">Password:</label>
          <%= Html.Password("password") %>
          <%= Html.ValidationMessage("password") %>
        </p>
        <p>
          <label for="confirmPassword">Confirm password:</label>
          <%= Html.Password("confirmPassword") %>
          <%= Html.ValidationMessage("confirmPassword") %>
        </p>
        
        <% 
          Dim registrationCaptcha As Lanap.BotDetect.MvcCaptcha
					
          registrationCaptcha = _ 
            New Lanap.BotDetect.MvcCaptcha("RegistrationCaptcha")
						
          registrationCaptcha.TextStyle = _ 
            Lanap.BotDetect.TextStyleEnum.Lego
						
          registrationCaptcha.CodeLength = 4
					
          registrationCaptcha.ImageSize = _ 
            New System.Drawing.Size(245, 50)
					
          registrationCaptcha.SaveProperties()
      
          If (Not registrationCaptcha.IsSolved) Then
        %>
				
        <%= Html.Captcha(registrationCaptcha) %>
        <p>
          <label for="captchaCode">Type the characters you see 
            in the picture:</label>
          <%= Html.TextBox("captchaCode") %>
          <%= Html.ValidationMessage("captchaCode")%>
        </p>
          <script type="text/javascript">
            function LBD_ClearUserInput() {
              var LBD_textBox = document.getElementById('captchaCode');
              if(LBD_textBox) {
                LBD_textBox.value = '';
              }
            }
            LBD_RegisterHandler('PreReloadCaptchaImage', 
              LBD_ClearUserInput);
          </script>				
        <% 
        End If
        %>
				
        <p>
          <input type="submit" value="Register" />
        </p>
      </fieldset>
    </div>
  <% End Using %>
</asp:Content>

Explicación

En vez de usar el control Lanap.BotDetect.Captcha que usamos para los formularios web de aplicaciones de ASP.NET, usaremos una clase especializada llamada Lanap.BotDetect.MvcCaptcha, y estableceremos sus propiedades directamente en el formulario del código mismo. Note que luego de configurar algunas propiedades, se debe llamar al método SaveProperties - de otra forma esta configuración no será almacenada de forma persistente.

Luego de inicializar la instancia de MvcCaptcha, pasaremos como parámetro un elemento HtmlHelper usado para generar el MvcCaptcha, pero sólo si no ha sido correctamente validado. En otras palabras, si el usuario escribe el Captcha correctamente, él ya no es considerado como posible "bot" automatizado, y un nuevo Captcha ya no es mostrado incluso para el mismo usuario en otras validaciones en que falle (como correo electrónico).

Es de considerar, que debimos utilizar <%@ Import, pues HtmlHelper debía ser definido.

Attributes/CaptchaValidationAttribute.vb

Código fuente completo

Imports System
Imports System.Web.Mvc
Imports Lanap.BotDetect

Namespace Attributes

    <AttributeUsage(AttributeTargets.Method, AllowMultiple:=False, _ 
      Inherited:=False)> _
    Public NotInheritable Class CaptchaValidationAttribute
        Inherits ActionFilterAttribute
        Public Sub New()
            Me.New("captchaCode", "RegistrationCaptcha")
        End Sub

        Public Sub New(ByVal inputfield As String, _
          ByVal captchaId As String)
            mField = inputfield
            mCaptchaId = captchaId
        End Sub

        Private mField As String
        Private mCaptchaId As String

        Public Overrides Sub OnActionExecuting(ByVal filterContext As _
          ActionExecutingContext)
            ' make sure the captcha valid key is not contained in the 
            ' route data
            If (filterContext.RouteData.Values.ContainsKey( _
              "captchaValid")) Then
                filterContext.RouteData.Values.Remove("captchaValid")
            End If

            If (String.IsNullOrEmpty(mCaptchaId)) Then
                filterContext.RouteData.Values.Add("captchaValid", _ 
                  False)
                Return
            End If

            Dim captchaCode As String
            captchaCode = filterContext.HttpContext.Request.Form(mField)

            ' Captcha validation
            Dim captchaInstance As MvcCaptcha
            captchaInstance = New MvcCaptcha(mCaptchaId)
            Dim captchaInstanceId As String
						
            captchaInstanceId = _ 
              filterContext.HttpContext.Request.Form( _
                captchaInstance.CaptchaIdKey)

            ' the Captcha is only validated if it was included on 
            ' the page
            If (Not (String.IsNullOrEmpty(captchaInstanceId))) Then
                If ((captchaInstance.Validate(captchaCode, _
                  captchaInstanceId))) Then
                    filterContext.RouteData.Values.Add("captchaValid", _
                      True)
                    Return
                End If
            End If
            filterContext.RouteData.Values.Add("captchaValid", False)
            Return
        End Sub
    End Class
End Namespace

Explicación

Para validar el CAPTCHA ingresado por el usuario, crearemos una nueva instancia de MvcCaptcha con el mismo nombre con el que fue añadido a Register.aspx page ("RegistrationCaptcha"), y la validaremos.

Comprobando la existencia del campo oculto llamado captchaInstanceId, podemos detectar la presencia del Captcha mismo en la página indirectamente. Esto nos permite validar el Captcha sólo en las ocasiones en que fue resuelto por el usuario.

El nombre usado para cada Captcha nos permite tener diferentes Captchas y validarlos de forma separada - por ejemplo, podríamos tener un Captcha en PostComment.aspx enlazado con otro controlador usando una acción llamada PostComment, y podriamos usar otro Captcha con otro nombre ("CommentCaptcha") para distinguirlos.

Controllers/AccountController.vb

Código fuente completo

Cómo este código fuente contiene un montón de código generado por el entorno de desarrollo, sólo mostraremos algunas partes que requieren modificaciones para que BotDetect CAPTCHA funcione correctamente.

<AcceptVerbs(HttpVerbs.Post)> _
<CaptchaValidation("captchaCode", "RegistrationCaptcha")> _
 Function Register(ByVal userName As String, ByVal email As String, 
  ByVal password As String, ByVal confirmPassword As String, 
  ByVal captchaCode As String) As ActionResult

  ViewData("Title") = "Register"
  ViewData("PasswordLength") = MembershipService.MinPasswordLength

  If ValidateRegistration(userName, email, password, confirmPassword, 
    captchaCode) Then
    ' Attempt to register the user
    Dim createStatus As MembershipCreateStatus = _ 
      MembershipService.CreateUser(userName, password, email)

    If createStatus = MembershipCreateStatus.Success Then
      FormsAuth.SignIn(userName, False)
      Return RedirectToAction("Index", "Home")
    Else
      ModelState.AddModelError("_FORM", _
        ErrorCodeToString(createStatus))
    End If
  End If

  Return View()
End Function
Private Function ValidateRegistration(ByVal userName As String, _
  ByVal email As String, ByVal password As String, _
  ByVal confirmPassword As String, ByVal captchaCode As String) _
  As Boolean
  If String.IsNullOrEmpty(userName) Then
    ModelState.AddModelError("username", _ 
      "You must specify a username.")
  End If

  If String.IsNullOrEmpty(email) Then
    ModelState.AddModelError("email", _ 
      "You must specify an email address.")
  End If

  If password Is Nothing OrElse _ 
     password.Length < MembershipService.MinPasswordLength Then
    ModelState.AddModelError("password", _ 
      String.Format(CultureInfo.CurrentCulture, _
      "You must specify a password of {0} or more characters.", _
      MembershipService.MinPasswordLength))
  End If

  If Not String.Equals(password, confirmPassword, _ 
    StringComparison.Ordinal) Then
    ModelState.AddModelError("_FORM", _
      "The new password and confirmation password do not match.")
  End If

  'Captcha validation
  If (Not (CType(Me.RouteData.Values("captchaValid"), Boolean))) Then
    ModelState.AddModelError("captchaCode", _
      "The CAPTCHA code was incorrect!")
  End If

  Return ModelState.IsValid
End Function

Explicación

Añadimos un nuevo recuadro de texto para que el usuario ingrese el CAPTCHA, por lo que debemos modificar los métodos Register y ValidateRegistration aceptando un string adicional - captchaCode. En el método ValidateRegistration, añadimos una comprobación del CAPTCHA mismo.

Views/Shared/Site.Master

Código fuente completo

<%@ Master Language="VB" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <meta http-equiv="Content-Type" 
    content="text/html; charset=iso-8859-1" />
  <asp:ContentPlaceHolder ID="head" runat="server">
    <title></title>
  </asp:ContentPlaceHolder>
  <asp:PlaceHolder runat="server" id="includes">
	
    <link href="<%= VirtualPathUtility.ToAbsolute(
      "~/Content/Site.css") %>" rel="stylesheet" type="text/css" />
			
    <link href="<%= VirtualPathUtility.ToAbsolute(
      "~/Content/BotDetectLayout.css") %>" rel="stylesheet" 
      type="text/css" />
			
    <script type='text/javascript' src='<%= VirtualPathUtility.
      ToAbsolute("~/Content/BotDetectScripts.js") %>'></script>
			
  </asp:PlaceHolder>
</head>

<body>
  <div class="page">

    <div id="header">
      <div id="title">
        <h1>My MVC Application</h1>
      </div>
        
      <div id="logindisplay">
        <% Html.RenderPartial("LogOnUserControl"); %>
      </div> 
      
      <div id="menucontainer">
      
        <ul id="menu"> 
          <li><%= Html.ActionLink("Home", "Index", "Home")%></li>
          <li><%= Html.ActionLink("About", "About", "Home")%></li>
        </ul>
      
      </div>
    </div>

    <div id="main">
      <asp:ContentPlaceHolder ID="MainContent" runat="server" />

      <div id="footer">
      </div>
    </div>
  </div>
</body>
</html>

Explicación

La única modificación de la "Master page" es la incorporación de código de lado del cliente de BotDetect y declaraciones de CSS, que deben ser copiadas a la carpeta Content. Todas incluyen la ruta completa (para no tener problemas con niveles superiores de la jerarquía), incorporados dentro de un PlaceHolder para evitar problemas existentes con el despliegue de ASP.NET MVC.

Views/Shared/HtmlHelper.vb

Código fuente completo

Imports System.Runtime.CompilerServices

Public Module CaptchaExtensions

  <Extension()> _
  Public Function Captcha(ByVal helper As HtmlHelper, _
    ByRef captchaInstance As Object) As String
  
    Dim captchaInst As Lanap.BotDetect.MvcCaptcha
    captchaInst = CType(captchaInstance, Lanap.BotDetect.MvcCaptcha)

    Dim markupBuilder As StringBuilder
    markupBuilder = New StringBuilder

    markupBuilder.Append("<div class='LBD_CaptchaDiv' style='width:")
		
    markupBuilder.Append(captchaInst.ImageSize.Width + 28)
		
    markupBuilder.Append("px; height: ")
		
    markupBuilder.Append(captchaInst.ImageSize.Height + 10)
		
    markupBuilder.Append("px'>" & ControlChars.NewLine)
		
    markupBuilder.Append("<div class='LBD_CaptchaImage' style='width:")
		
    markupBuilder.Append(captchaInst.ImageSize.Width)
		
    markupBuilder.Append("px; height: ")
		
    markupBuilder.Append(captchaInst.ImageSize.Height)
		
    markupBuilder.Append("px '>" & ControlChars.NewLine)
		
    markupBuilder.Append("<img id='")
		
    markupBuilder.Append(captchaInst.CaptchaId)
		
    markupBuilder.Append("_CaptchaImage' src='")
		
    markupBuilder.Append(captchaInst.ImageLink)
		
    markupBuilder.Append("' alt='CAPTCHA Code Image' />" & _
      ControlChars.NewLine)
			
    markupBuilder.Append("</div>" & ControlChars.NewLine)
		
    markupBuilder.Append("<div class='LBD_CaptchaIcons'>")
		
    markupBuilder.Append("<a href="")
		
    markupBuilder.Append(captchaInst.SoundLink)
		
    markupBuilder.Append("' onclick='LBD_LoadSound(""")
		
    markupBuilder.Append(captchaInst.CaptchaId)
		
    markupBuilder.Append("_SoundPlaceholder"", """)
		
    markupBuilder.Append(captchaInst.SoundLink)
		
    markupBuilder.AppendFormat(""");this.blur();return false;'>
      <img src='{0}' alt='Play Sound' /></a>", _ 
      VirtualPathUtility.ToAbsolute("~/Content/Speaker.gif"))
		
    markupBuilder.Append("<a href="#" onclick='LBD_ReloadImage(""")
		
    markupBuilder.Append(captchaInst.CaptchaId)
		
    markupBuilder.AppendFormat("_CaptchaImage"");this.blur();
      return false;'><img src='{0}' alt='Change the code' />
      </a>", VirtualPathUtility.ToAbsolute("~/Content/Refresh.gif"))
		
    markupBuilder.Append("<div id='")
		
    markupBuilder.Append(captchaInst.CaptchaId)
		
    markupBuilder.Append("_SoundPlaceholder' class='LBD_placeholder'>
      &nbsp;</div>")
		
    markupBuilder.Append("</div>" & ControlChars.NewLine)
		
    markupBuilder.Append("</div>" & ControlChars.NewLine)
		
    markupBuilder.Append("<input type='hidden' ")
		
    markupBuilder.Append("id='")
		
    markupBuilder.Append(captchaInst.CaptchaIdKey)
		
    markupBuilder.Append("' ")
		
    markupBuilder.Append("name='")
		
    markupBuilder.Append(captchaInst.CaptchaIdKey)
		
    markupBuilder.Append("' ")
		
    markupBuilder.Append("value='")
		
    markupBuilder.Append(captchaInst.CurrentInstanceId)
		
    markupBuilder.Append("' />")

    Return markupBuilder.ToString()

  End Function

End Module

Explicación

En este archivo, incluimos un método simple llamado MvcCaptcha para generar todos los formularios ASP.NET MVC. Note que el ícono del altavoz y recarga fueron cargados desde el directorio Content.

Content/BotDetectLayout.css

Código fuente completo

.LBD_CaptchaDiv {
  padding: 0 !important;
  margin: 20px 0 0 10px;
  overflow: visible !important;
}

*html .LBD_CaptchaDiv {
  margin-bottom: -3px !important;
  overflow: hidden !important;
}

.LBD_CaptchaDiv .LBD_CaptchaImage
{
  float: left !important;
  margin: 0 !important;
  padding: 0 !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons 
{
  width: 22px !important;
  float: right !important;
  text-align: left !important;
  margin: 0 !important;
  padding: 0 !important;
  margin-bottom: -4px !important;
  margin-top: -1px !important;
  margin-right: 2px !important;
}

*html .LBD_CaptchaDiv .LBD_CaptchaIcons 
{
  margin-right: 1px !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a
{
  margin: 0 !important;
  padding: 0 !important;
  display: block !important;
  background-color: transparent !important;
  text-decoration: none !important;
  border: none !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a:focus, 
.LBD_CaptchaDiv .LBD_CaptchaIcons a:active
{
  border: none !important;
  -moz-outline:none !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a img
{
  border: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  margin-top: 2px !important;
  margin-bottom: 3px !important;
  display: block !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a:focus img, 
.LBD_CaptchaDiv .LBD_CaptchaIcons a:active img
{
  border: 1px dotted #333 !important;
  margin: 0 !important;
  padding: 0 !important;
  margin-top: 1px !important;
  margin-bottom: 1px !important;
  display: block !important;
}

*html .LBD_CaptchaDiv .LBD_CaptchaIcons a:focus img, 
*html .LBD_CaptchaDiv .LBD_CaptchaIcons a:active img
{
  border: 1px solid #999 !important;
  margin-bottom: 3px !important;
}

*:first-child+html .LBD_CaptchaDiv .LBD_CaptchaIcons a:focus img, 
*:first-child+html .LBD_CaptchaDiv .LBD_CaptchaIcons a:active img
{
  border: 1px solid #999 !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons .LBD_placeholder
{
  visibility: hidden !important; 
  width: 0 !important; 
  height: 0 !important;
}

*html .LBD_CaptchaDiv .LBD_CaptchaIcons .LBD_placeholder
{
  display: none !important; 
}

*:first-child+html .LBD_CaptchaDiv .LBD_CaptchaIcons .LBD_placeholder
{
  display: none !important; 
}

Explicación

En las aplicaciones ASP.NET, las declaraciones CSS usadas por el control BotDetect Captcha control se incluyen dentro del ensamblado Lanap.BotDetect.dll y incluyen recursos web dentro. Como este alcance no está disponible en ASP.NET MVC, el mismo CSS debe ser declarado manualmente en un archivo .css externo.

Content/BotDetectScripts.js

Código fuente completo

var LBD_ImgId = null;
var LBD_Img = null;
var LBD_NewImg = null; 
var LBD_Parent = null;
var LBD_Prompt = null;
var LBD_Timer = null;
var LBD_TimerTicks = 0;

function LBD_LoadSound(soundPlaceholderId, soundLink)
{
  if(document.getElementById)
  {
    var i = soundLink.indexOf('&d=');
    if (-1 != i) {
      soundLink = soundLink.substring(0, i);
    }
    
    soundLink = soundLink + '&d=' + LBD_GetTimestamp();
    
    if ((-1 == soundLink.indexOf('&e=')) && 
        (document.location.protocol == "https:")) {
      soundLink = soundLink + '&e=1';
    }

    var placeholder = document.getElementById(soundPlaceholderId);
    var objectSrc = "&lt;object id='captchaSound' 
      classid='clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95' 
      height='0' width='0' style='width:0; height:0;'&gt;
      &lt;param name='AutoStart' value='1' /&gt;
      &lt;param name='Volume' value='0' /&gt;
      &lt;param name='PlayCount' value='1' /&gt;
      &lt;param name='FileName' value='" + soundLink + "' /&gt;
      &lt;embed id='captchaSoundEmbed' src='"+ soundLink + 
        "' autoplay='true' hidden='true' volume='100' 
        type='"+ LBD_GetMimeType() +"' style='display:inline;' /&gt;
    &lt;/object&gt;";

    placeholder.innerHTML = "";
    placeholder.innerHTML = objectSrc;
  }
}

function LBD_GetTimestamp() {
  var d = new Date();
  var t = d.getTime() + (d.getTimezoneOffset() * 60000);
  return t;
}

function LBD_GetMimeType()
{
  var mimeType = "audio/x-wav";
  return mimeType;
}

function LBD_ReloadImage(imgId)
{ 
  if(imgId)
  {
    LBD_ImgId = imgId;
    LBD_Img = document.getElementById(LBD_ImgId);
    var src = LBD_Img.src;
    
    var i = src.indexOf('&d=');
    if (-1 != i) {
      src = src.substring(0, i);
    }
    var newSrc = src + '&d=' + LBD_GetTimestamp();

    LBD_NewImg = document.createElement('img');
    LBD_NewImg.onload = LBD_ShowImage;
    LBD_NewImg.id = LBD_Img.id;
    LBD_NewImg.alt = LBD_Img.alt;
    LBD_NewImg.src = newSrc;

    LBD_Prompt = document.createElement('span');
    LBD_Prompt.appendChild(document.createTextNode('.'));

    LBD_PreReloadImage();
    LBD_Parent = LBD_Img.parentNode;
    LBD_Parent.removeChild(LBD_Img);
    LBD_Parent.appendChild(LBD_Prompt);

    LBD_ShowProgress()
  }
}	

function LBD_ShowProgress()
{
  if((LBD_Prompt)&&(LBD_TimerTicks < 100))
  {
    LBD_TimerTicks = LBD_TimerTicks + 1;

    if(0 == LBD_TimerTicks % 5)
    {
      LBD_Prompt.firstChild.nodeValue = '.';     
    }
    else
    {
      LBD_Prompt.firstChild.nodeValue = 
        LBD_Prompt.firstChild.nodeValue + '.';
    }

    LBD_Timer = setTimeout("LBD_ShowProgress()", 250);
  }
  else
  {
    clearTimeout(LBD_Timer);
    LBD_TimerTicks = 0;
  }
}

function LBD_ShowImage()
{
  if(LBD_NewImg && LBD_Parent && LBD_Prompt)
  {
    LBD_Parent.removeChild(LBD_Prompt);
    LBD_Parent.appendChild(LBD_NewImg);
    LBD_Prompt = null;
    LBD_PostReloadImage();
  }
}

function LBD_PreReloadImage() {}
function LBD_PostReloadImage() {}

function LBD_RegisterHandler(eventName, userHandler)
{
  switch(eventName.toLowerCase())
  {
    case 'prereloadcaptchaimage':
      var LBD_OldHandler = LBD_PreReloadImage;
      LBD_PreReloadImage = function() {
        LBD_OldHandler();
        userHandler();
      }
      break;
    case 'postreloadcaptchaimage':
      var oldHandler = LBD_PostReloadImage;
      LBD_PostReloadImage = function() {
        LBD_OldHandler();
        userHandler();
      }
      break;
  }
}

if ((typeof(Sys) != "undefined") && 
    (typeof(Sys.Application) != "undefined")) {
  Sys.Application.notifyScriptLoaded();
}

Explicación

En las aplicaciones ASP.NET, las rutinas de lado del cliente en Javascript usadas por el control BotDetect Captcha control se incluyen dentro del ensamblado Lanap.BotDetect.dll y incluyen recursos web dentro. Como este alcance no está disponible en ASP.NET MVC, el mismo código de Javascript debe ser declarado manualmente en un archivo .js externo.

Global.asax.vb

Código fuente completo

' Note: For instructions on enabling IIS6 or IIS7 classic mode, 
' visit http://go.microsoft.com/?LinkId=9394802

Public Class MvcApplication
  Inherits System.Web.HttpApplication

  Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

    ' MapRoute takes the following parameters, in order:
    ' (1) Route name
    ' (2) URL with parameters
    ' (3) Parameter defaults
    routes.MapRoute( _
      "Default", _
      "{controller}/{action}/{id}", _
      New With {.controller = "Account", .action = "Register", .id = ""} _
    )

    routes.MapRoute( _
      "Root", _
      "", _
      New With {.controller = "Account", .action = "Register", .id = ""} _
    )

  End Sub

  Sub Application_Start()
    RegisterRoutes(RouteTable.Routes)
  End Sub
End Class

Explicación

Para registrar la acción por defecto de la aplicación, "Register", debemos (para que el CAPTCHA sea visible automáticamente al inicio de la aplicación misma) debemos cambiar la ruta del método RegisterRoutes.

Web.config

Código fuente completo

<?xml version="1.0"?>
<configuration>

  <configSections>
    <sectionGroup name="system.web.extensions" type="
      System.Web.Configuration.SystemWebExtensionsSectionGroup, 
      System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35">
      <sectionGroup name="scripting" type="System.Web.
        Configuration.ScriptingSectionGroup, System.Web.Extensions, 
        Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35">
        <section name="scriptResourceHandler" type="System.Web.
          Configuration.ScriptingScriptResourceHandlerSection, 
          System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35" requirePermission="false"
          allowDefinition="MachineToApplication"/>
        <sectionGroup name="webServices" type="System.Web.
          Configuration.ScriptingWebServicesSectionGroup, 
          System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35">
          <section name="jsonSerialization" type="System.Web.
            Configuration.ScriptingJsonSerializationSection, 
            System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="Everywhere" />
          <section name="profileService" type="System.Web.
            Configuration.ScriptingProfileServiceSection, 
            System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="MachineToApplication" />
          <section name="authenticationService" type="System.Web.
            Configuration.ScriptingAuthenticationServiceSection, 
            System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="MachineToApplication" />
          <section name="roleService" type="System.Web.Configuration.
            ScriptingRoleServiceSection, System.Web.Extensions, 
            Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="MachineToApplication" />
        </sectionGroup>
      </sectionGroup>
    </sectionGroup>
  </configSections>

  <appSettings>
    <add key="LBD_RequestPath" value="LanapCaptcha.axd" />
  </appSettings>

  <connectionStrings>
    <add name="ApplicationServices" connectionString="
      data source=.\SQLEXPRESS;Integrated Security=SSPI;
      AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" 
      providerName="System.Data.SqlClient"/>
  </connectionStrings>

  <system.web>
    <compilation debug="false">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Abstractions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Routing, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Mvc, Version=1.0.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Xml.Linq, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Data.Linq, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089" />
      </assemblies>
    </compilation>

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" />
    </authentication>

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider"
          type="System.Web.Security.SqlMembershipProvider, 
            System.Web, Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a"
          connectionStringName="ApplicationServices"
          enablePasswordRetrieval="false"
          enablePasswordReset="true"
          requiresQuestionAndAnswer="false"
          requiresUniqueEmail="false"
          passwordFormat="Hashed"
          maxInvalidPasswordAttempts="5"
          minRequiredPasswordLength="6"
          minRequiredNonalphanumericCharacters="0"
          passwordAttemptWindow="10"
          passwordStrengthRegularExpression=""
          applicationName="/"
        />
      </providers>
    </membership>

    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider"
          type="System.Web.Profile.SqlProfileProvider, System.Web, 
            Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a"
          connectionStringName="ApplicationServices"
          applicationName="/"
        />
      </providers>
    </profile>

    <roleManager enabled="false">
      <providers>
        <clear />
        <add connectionStringName="ApplicationServices" 
          applicationName="/" name="AspNetSqlRoleProvider" 
          type="System.Web.Security.SqlRoleProvider, System.Web, 
            Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a" />
        <add applicationName="/" name="AspNetWindowsTokenRoleProvider" 
          type="System.Web.Security.WindowsTokenRoleProvider, 
            System.Web, Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a" />
      </providers>
    </roleManager>

    <pages>
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" 
          assembly="System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" 
          assembly="System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      </controls>

      <namespaces>
        <add namespace="System.Web.Mvc"/>
        <add namespace="System.Web.Mvc.Ajax"/>
        <add namespace="System.Web.Mvc.Html"/>
        <add namespace="System.Web.Routing"/>
        <add namespace="System.Linq"/>
        <add namespace="System.Collections.Generic"/>
      </namespaces>
    </pages>

    <sessionState mode="InProc" cookieless="AutoDetect" timeout="20"
      sessionIDManagerType="Lanap.BotDetect.Persistence.
      CustomSessionIDManager, Lanap.BotDetect" />

    <customErrors mode="On" defaultRedirect="~/Content/Error.html"/>

    <httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="LanapCaptcha.axd" 
        type="Lanap.BotDetect.CaptchaHandler, Lanap.BotDetect" />
      <add verb="*" path="*.asmx" validate="false" 
        type="System.Web.Script.Services.ScriptHandlerFactory, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add verb="*" path="*_AppService.axd" validate="false" 
        type="System.Web.Script.Services.ScriptHandlerFactory, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add verb="GET,HEAD" path="ScriptResource.axd" 
        type="System.Web.Handlers.ScriptResourceHandler, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35" validate="false"/>
      <add verb="*" path="*.mvc" validate="false" 
        type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, 
        Version=1.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
    </httpHandlers>

    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.
        UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, 
        Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </httpModules>

  </system.web>

  <system.codedom>
    <compilers>
      <compiler language="VB.NET;cs;VBNet" extension=".cs" 
        warningLevel="4" type="Microsoft.VBNet.VBNetCodeProvider, 
          System, Version=2.0.0.0, Culture=neutral, 
          PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>

      <compiler language="vb;vbs;visualbasic;vbscript" 
        extension=".vb" warningLevel="4" type="Microsoft.VisualBasic.
          VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, 
          PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="OptionInfer" value="true"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>
    </compilers>
  </system.codedom>

  <system.web.extensions/>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>

    <modules runAllManagedModulesForAllRequests="true">
      <remove name="ScriptModule" />
      <remove name="UrlRoutingModule" />
      <add name="ScriptModule" preCondition="managedHandler" 
        type="System.Web.Handlers.ScriptModule, System.Web.Extensions, 
        Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.
        UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, 
        Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </modules>

    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <remove name="ScriptHandlerFactory" />
      <remove name="ScriptHandlerFactoryAppServices" />
      <remove name="ScriptResource" />
      <remove name="MvcHttpHandler" />
      <remove name="UrlRoutingHandler" />
      <remove name="LanapCaptchaHandler" />
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" 
        preCondition="integratedMode" type="System.Web.Script.
          Services.ScriptHandlerFactory, System.Web.Extensions,
          Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" 
        path="*_AppService.axd" preCondition="integratedMode"
        type="System.Web.Script.Services.ScriptHandlerFactory, 
          System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptResource" preCondition="integratedMode" 
        verb="GET,HEAD" path="ScriptResource.axd" 
        type="System.Web.Handlers.ScriptResourceHandler, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35" />
      <add name="MvcHttpHandler" preCondition="integratedMode" 
        verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, 
        System.Web.Mvc, Version=1.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingHandler" preCondition="integratedMode" 
        verb="*" path="UrlRouting.axd" type="System.Web.
          HttpForbiddenHandler, System.Web, Version=2.0.0.0, 
          Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      <add name="LanapCaptchaHandler" verb="*" 
        path="LanapCaptcha.axd" type="Lanap.BotDetect.CaptchaHandler, 
        Lanap.BotDetect" />

    </handlers>
  </system.webServer>

</configuration>

Explicación

El primer elemento que hay que añadir a Web.config file is the LBD_RequestPath está dentro de la declaración de <appSettings>. Esto es para configurar la ruta de BotDetect CAPTCHA con el fin de utilizar la extensión .axd extension, como sucede por defecto, con la extensión .aspx, debemos redirigir las consultas al código de ASP.NET MVC.

El siguiente cambio ocurre en la sección <sessionState>, en donde registramos un sessionIDManager personalizado, para asegurar que el audio del CAPTCHA funcione para navegadores Google Chrome y IE 7.0 + sobre Vista.

La sección <customErrors> es simplemente usada para mostrar posibles errores con rutas y situaciones comunes mostradas en la ayuda de ASP.NET MVC, considereando que no tener ASP.NET MVC instalado es el error más ocurrido que puede hacer fallar el proyecto de ejemplo actual. Si tiene ASP.NET MVC actualmente en su sistema y es de igual forma redirigido a la página Error.html, por favor descomente la línea de <customErrors> para saber con exactitud el error ocurrido.

Finalmente, registraremos un HttpHandler que BotDetect utiliza para funciones de imagen y audio dentro de <httpHandlers> y en las secciones <system.webServer>/<handlers> (así nos aseguramos que el CAPTCHA funcione correctamente dentro de IIS 7.0 y versiones más nuevas). Hemos modificado el manejo de la extensión .axd para motivos del funcionamiento de este ejemplo, como explicamos anteriormente.

language: English Español Tiếng Việt