ASP.NET jQuery Validation CAPTCHA VB.NET Code Example

This example project shows how to integrate BotDetect ASP.NET CAPTCHA validation with jQuery Validation client-side form validation.

First Time Here?

Check the BotDetect ASP.NET WebForms Captcha Quickstart for key integration steps.

Client-side validation is not secure by itself (it can be bypassed trivially by bots that don't execute JavaScript at all), so the example shows how the protected form action must always be secured by server-side CAPTCHA validation first, and uses client-side validation only to improve the user experience.

Download the BotDetect ASP.NET CAPTCHA Generator archive to run this example
  • C#
  • VB.NET

Within this page, the root folder of the extracted archive is referred as the <BDC-DIR>.

This example is in the <BDC-DIR>/lgcy-on-lgcy/examples/t_api-captcha-webforms-api_basics-unobtrusive/vbnet/ folder; and contains the following files:

Default.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" 
Inherits="_Default" %>

<!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 id="Head1" runat="server">
  <title>BotDetect ASP.NET CAPTCHA jQuery Validation Example</title>
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" />
  <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
  <script type="text/javascript" src="js/jquery.validate.min.js"></script>
</head>
<body>
  <form id="form1" runat="server">
    <h1>BotDetect ASP.NET CAPTCHA jQuery Validation Example</h1>
    <fieldset>
      <legend>CAPTCHA Validation</legend>
      <p class="prompt">
        <label for="CaptchaCodeTextBox">Retype the characters from the picture:</label></p>
      <BotDetect:WebFormsCaptcha ID="ExampleCaptcha" runat="server" />
      <div class="validationDiv">
        <!-- add CssClass name used to set up jQuery field validation -->
        <asp:TextBox ID="CaptchaCodeTextBox" runat="server" 
        CssClass="captchaVal"></asp:TextBox>
        <br />
        <asp:Button ID="ValidateCaptchaButton" runat="server" />
      </div>
      <asp:Label ID="CaptchaCorrectLabel" 
      AssociatedControlID="CaptchaCodeTextBox" runat="server" CssClass="correct">
      </asp:Label>
      <asp:Label ID="CaptchaIncorrectLabel" 
      AssociatedControlID="CaptchaCodeTextBox" runat="server" 
      CssClass="incorrect"></asp:Label>
    </fieldset>

    <script type="text/javascript" src="js/validationRules.js"></script>
  </form>
</body>
</html>

The input form code is almost exactly the same as in the basic Captcha example. The only differences are that we add a custom CssClass attribute to the Captcha code input textbox, and that we must include jQuery, the jQuery.validate plugin, and the script containing our custom validation rules. To make the example work in IE 6, we have to load the jQuery includes before Captcha markup.

The custom Css class value is used to ensure we can reliably identify the Captcha textbox from client-side code. Since ASP.NET controls don't necessarily have predictable client-side identifiers (depending on the ClientIDMode setting used, whether the control is placed within a naming container, a data-bound repeater etc.), a unique Css class is used instead.

js\validationRules.js

$(document).ready(function () {

  $("#form1").validate({
    // the Captcha input must only be validated when the whole code string is
    // typed in, not after each individual character (onkeyup must be false)
    onkeyup: false,
    // since the example form only has one input element, it doesn't make sense
    // to validate it immediately after it loses focus
    onfocusout: false,
    // customize client-side error display elements
    errorClass: "incorrect",
    validClass: "correct",
    errorElement: "label",
    // always reload the Captcha image if remote validation failed,
    // since it will not be usable any more (a failed validation attempt
    // removes the attempted code for necessary Captcha security
    showErrors: function (errorMap, errorList) {
      this.defaultShowErrors();
      for (var i = 0; i < errorList.length; i++) {
        var element = errorList[i].element;
        var message = errorList[i].message;
        // check element css class and does the error message match remote
        // validation failure
        if (element.className.match(/captchaVal/) &&
            message === this.settings.messages[element.id].remote) {
          element.Captcha.ReloadImage();
        }
      }
    }
  });

  // add validation rules by CSS class, so we don't have to know the
  // exact client id of the Captcha code textbox
  $(".captchaVal").rules('add', {
    required: true,
    remote: $(".captchaVal").get(0).Captcha.ValidationUrl,
    messages: {
      required: 'Required (client)',
      remote: 'Incorrect (client)'
    }
  });
});

To set up jQuery validation of a textbox taking Captcha code input, we do the following:

  • Add validation rules specifying it as a required field, that also needs to be validated remotely when a value is entered; define error messages shown when these validation rules are triggered
  • Disable onkeyup validation of the Captcha code, since we must validate the whole code and not the individual characters
  • Reload the Captcha image whenever remote Captcha validation fails

CAPTCHA jQuery Validation Rules and Error Messages

Since we're not using the input textbox Id for jQuery validation rules, we need to add them dynamically after the default validator has been initialized; that's why they're located after the $("#form1").validate({ call.

The expression for the remote Captcha validation Url can easily be read looking at dot-separated segments from right to left:

  • The ValidationUrl property is part of the BotDetect client-side API, and is unique for each Captcha instance.
  • The client-side BotDetect object can always be accessed through the Captcha property of the Captcha code textbox. For this custom property to be assigned, the textbox must be registered during server-side Captcha object initialization, through the UserInputID property (as has been done in Default.aspx.cs code below).
  • We use the standard jQuery selector to access the textbox by Css class, and the .get(0) function call to get the underlying DOM element. This is needed because BotDetect adds the custom Captcha property to the DOM element directly, and the jQuery wrapper element returned by the jQuery selector doesn't include it.

Disabling Remote CAPTCHA Validation on Individual Character Input

Since BotDetect deletes the Captcha code stored on the server after failed validation of a particular Captcha instance (as explained in the BotDetect FAQ), we must avoid validating the user input before the user finished typing in the whole Captcha code. The simplest way to achieve this is to disable onkeyup validation completely (onkeyup: false).

Reloading the CAPTCHA Image When Remote Validation Fails

Due to the above (failed validation invalidates the stored code for security purposes), we must also always reload the Captcha image when remote validation fails. Otherwise the user would be trying to correct his input based on an expired image, and couldn't pass Captcha validation at all.

We do this by customizing the jQuery.validate showErrors callback: beside the regular functionality (this.defaultShowErrors), we also check that there is a Captcha validation error (element.className.match(/captchaVal/) and that the error message indicates a remote validation failure (message === this.settings.messages[element.id]. remote). If that is case, we call the ReloadImage() function on the client-side Captcha object (accessed through the textbox element as explained above).

Default.aspx.vb

Partial Class _Default
  Inherits System.Web.UI.Page

  Protected Sub Page_PreRender(ByVal sender As Object, 
  ByVal e As System.EventArgs) Handles Me.PreRender
    ' initial page setup
    If Not IsPostBack Then
      ' set control text
      ValidateCaptchaButton.Text = "Validate"
      CaptchaCorrectLabel.Text = "Successful form submission (server)"
      CaptchaIncorrectLabel.Text = "Failed form submission (server)"

      ' these messages are shown only after validation
      CaptchaCorrectLabel.Visible = False
      CaptchaIncorrectLabel.Visible = False
    End If

    ' setup client-side input processing
    ExampleCaptcha.UserInputID = CaptchaCodeTextBox.ClientID

    If IsPostBack Then
      ' validate the Captcha to check we're not dealing with a bot
      Dim code As String, isHuman As Boolean
      code = CaptchaCodeTextBox.Text.Trim().ToUpper()
      isHuman = ExampleCaptcha.Validate(code)
      CaptchaCodeTextBox.Text = "" ' clear previous user input

      If isHuman Then
        CaptchaCorrectLabel.Visible = True
        CaptchaIncorrectLabel.Visible = False
      Else
        CaptchaCorrectLabel.Visible = False
        CaptchaIncorrectLabel.Visible = True
      End If
    End If
  End Sub

End Class



The input form code is functionally identical to the basic Captcha example, with only the Captcha validation error and success messages changed to make it easier to follow the interaction between client-side and server-side validation.

We left the server-side Captcha validation (implemented in the basic Captcha example) in place, ensuring that any bots or users with JavaScript disabled have their input checked. For users with JavaScript enabled, errors in Captcha code input will be shown on the client without full form postback to the server.

Web.config

<?xml version="1.0"?>
<!--
  For more information on how to configure your ASP.NET application, please 
  visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <section name="botDetect" requirePermission="false" 
    type="BotDetect.Configuration.BotDetectConfigurationSection, BotDetect"/>
  </configSections>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
    <add key="ValidationSettings:UnobtrusiveValidationMode" value="None"/>
  </appSettings>
  <system.web>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="BotDetectCaptcha.ashx" 
      type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </httpHandlers>
    <!-- Register a custom SessionIDManager for BotDetect Captcha requests -->
    <sessionState mode="InProc" cookieless="AutoDetect" timeout="20" 
    sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, BotDetect"/>
    <!-- Session state is required for BotDetect storage; you can also turn if off 
    globally and only enable for BotDetect-protected pages if you prefer -->
    <pages controlRenderingCompatibilityVersion="4.0" enableSessionState="true">
      <controls>
      <!-- Register the BotDetect tag prefix for easier use in all pages -->
      <add assembly="BotDetect" namespace="BotDetect.Web.UI" 
      tagPrefix="BotDetect"/>
      </controls>
    </pages>
    <compilation debug="false" targetFramework="4.5"/>
    <httpRuntime requestValidationMode="4.5" targetFramework="4.5" 
    encoderType="System.Web.Security.AntiXss.AntiXssEncoder, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
    <machineKey compatibilityMode="Framework45"/>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <handlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests (IIS 7.0+) -->
      <remove name="BotDetectCaptchaHandler"/>
      <add name="BotDetectCaptchaHandler" preCondition="integratedMode" verb="GET" 
      path="BotDetectCaptcha.ashx" type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
  <botDetect helpLinkEnabled="true" helpLinkMode="image" />
</configuration>

There are several BotDetect-related changes in the web.config file, including Captcha HttpHandler registration, ASP.NET Session state configuration, and BotDetect tag prefix registration.