React Form CAPTCHA Code Example

  1. Client-side
  2. Server-side

I. Client-side

The React Form Captcha code example shows how to add BotDetect CAPTCHA protection to a typical React form.

Captcha validation is integrated with other form fields validation, and only submissions that meet all validation criteria are accepted.

This kind of validation could be used on various types of public forms which accept messages, and are at risk of unwanted automated submissions.

For example, it could be used to ensure bots can't submit anything to a contact form, add guestbook entries, blog post comments or anonymous message board / forum replies.

Download the BotDetect ASP.NET CAPTCHA Library and run this example

Installed Location

By default, the React Form CAPTCHA code example project is installed at:
C:\Program Files\Captcha Inc\BotDetect 4 CAPTCHA Component\Asp.Net\.NET\WebApp\SimpleAPI\ReactCaptchaExample

You can also run it from the BotDetect Start Menu:
Programs > Captcha Inc > BotDetect 4 CAPTCHA Component > ASP.NET > ASP.NET Examples > Run .NET Examples

App.js

import React, { Component } from 'react';
import { HashRouter } from 'react-router-dom'
import BaseLayout from './BaseLayout.jsx';
import { captchaSettings } from 'reactjs-captcha';


class App extends Component {

  constructor(props) {
    super(props);

    captchaSettings.set({
      captchaEndpoint: 'BotDetectCaptcha.ashx'
    });
  }


  render() {
    return (
      <HashRouter>
        <BaseLayout />
      </HashRouter>
    );
  }
}

export default App;

We first import the Captcha component in the React component and use captchaSettings.set() to configure BotDetect ASP.NET Captcha path in captchaEndpoint setting in constructor method.

contact.jsx

import React from 'react';
import axios from 'axios';
import { Captcha } from 'reactjs-captcha';

class Contact extends React.Component {

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    let self = this;

    // error messages of input fields
    const errorMessages = {
      name: 'Name must be at least 3 characters.',
      email: 'Email is invalid.',
      subject: 'Subject must be at least 10 characters.',
      message: 'Message must be at least 10 characters.',
      captchaCode: 'Invalid code.'
    };

    // global variables that holds validation status of captcha input field,
    // use them for checking validation status when form is submitted
    this.isNameValid = false;
    this.isEmailValid = false;
    this.isSubjectValid = false;
    this.isMessageValid = false;


    function validateName() {
      var name = document.getElementById('name').value;
      self.isNameValid = (name.length >= 3);
      if (self.isNameValid) {
        document.getElementsByClassName('name')[0].innerHTML = '';
      } else {
        document.getElementsByClassName('name')[0].innerHTML = errorMessages.name;
      }
    }

    function validateEmail() {
      var email = document.getElementById('email').value;
      var emailRegEx = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
      self.isEmailValid = emailRegEx.test(email);
      if (self.isEmailValid) {
        document.getElementsByClassName('email')[0].innerHTML = '';
      } else {
        document.getElementsByClassName('email')[0].innerHTML = errorMessages.email;
      }
    }

    function validateSubject() {
      var subject = document.getElementById('subject').value;
      self.isSubjectValid = (subject.length >= 10);
      if (self.isSubjectValid) {
        document.getElementsByClassName('subject')[0].innerHTML = '';
      } else {
        document.getElementsByClassName('subject')[0].innerHTML = errorMessages.subject;
      }
    }

    function validateMessage() {
      var message = document.getElementById('message').value;
      self.isMessageValid = (message.length >= 10);
      if (self.isMessageValid) {
        document.getElementsByClassName('message')[0].innerHTML = '';
      } else {
        document.getElementsByClassName('message')[0].innerHTML = errorMessages.message;
      }
    }

    // validate input fields on blur event'
    document.getElementById('name').addEventListener('blur', validateName);
    document.getElementById('email').addEventListener('blur', validateEmail);
    document.getElementById('subject').addEventListener('blur', validateSubject);
    document.getElementById('message').addEventListener('blur', validateMessage);

    // UI captcha validation on blur event by using the custom 'validatecaptcha' event
    // and checking the 'event.detail' variable to either show error messages
    // or check captcha code input field status when form is submitted
    document.getElementById('captchaCode').addEventListener('validatecaptcha', function (event) {
      // update validation status of captcha code input
      let isCaptchaCodeCorrect = event.detail;
      // display or remove error message
      if (isCaptchaCodeCorrect) {
        document.getElementsByClassName('captcha-code-error')[0].innerHTML = '';
      } else {
        document.getElementsByClassName('captcha-code-error')[0].innerHTML = errorMessages.captchaCode;
      }
    });
  }

  submitForm(event) {
    let self = this;
    
    // use validateUnsafe() method to perform client-side captcha validation
    this.captcha.validateUnsafe(function(isCaptchaCodeCorrect) {

      if (isCaptchaCodeCorrect && self.isNameValid && self.isEmailValid && self.isSubjectValid && self.isMessageValid) {
       
        // form is valid
        // we send contact data as well as captcha data to server-side for
        // validating once again before they are inserted into database
      
        // captcha id for validating captcha at server-side
        var captchaId = self.captcha.getCaptchaId();

        // captcha code input value for validating captcha at server-side
        var captchaCode = self.captcha.getCaptchaCode();

        var postData = {
          name: document.getElementById('name').value,
          email: document.getElementById('email').value,
          subject: document.getElementById('subject').value,
          message: document.getElementById('message').value,
          captchaId: captchaId,
          captchaCode: captchaCode
        };

        axios.post('form/ContactHandler.ashx', postData)
          .then(response => {
            if (response.data.success) {
              // captcha, other form data passed and the data is also stored in database
              // show success message
              document.getElementById('form-messages').setAttribute('class', 'alert alert-success');
              document.getElementById('form-messages').innerHTML = 'Your message was sent successfully!';
            } else {
              // form validation failed
              document.getElementById('form-messages').setAttribute('class', 'alert alert-error');
              document.getElementById('form-messages').innerHTML = 'An error occurred while sending your message, please try again.';
            }
            self.captcha.reloadImage();
          }).catch(error => {
            throw new Error(error);
          });
      } else {
        // form is invalid
        document.getElementById('form-messages').setAttribute('class', 'alert alert-error');
        document.getElementById('form-messages').innerHTML = 'Please enter valid values.';
      }

    });

    event.preventDefault();
  }

  render() {
    var self = this;
    return (
      <div id="main-content">
        <form id="contactForm" method="POST" onSubmit={self.submitForm.bind(self)}>
          <div id="form-messages"></div>

          <label>
            <span>Name:</span>
            <input type="text" id="name" name="name"/>
          </label>
          <div className="error name"></div>


          <label>
            <span>Email</span>
            <input type="email" id="email" name="email"/>
          </label>
          <div className="error email"></div>


          <label>
            <span>Subject:</span>
            <input type="text" id="subject" name="subject"/>
          </label>
          <div className="error subject"></div>


          <label>
            <span>Message:</span>
            <textarea id="message" name="message"></textarea>
          </label>
          <div className="error message"></div>


          <Captcha styleName="reactFormCaptcha" ref={(captcha) => {this.captcha = captcha;}} />

          <label>
            <span>Retype the characters from the picture:</span>
            <input type="text" name="captchaCode" id="captchaCode" data-correct-captcha/>
          </label>
          <div className="error captcha-code-error"></div>

          <button type="submit" id="submitButton" className="btn btn-primary">Send
          </button>
        </form>
      </div>
    )
  }
}

export default Contact;

Just like React Basic Captcha example, we also need to add Captcha protection to the form by redering the Captcha component, as well as use ref attribute to access Captcha element in order to get Captcha client-side object when form is submitted.

In captcha code input element, we also add data-correct-captcha attribute. BotDetect Captcha React component will then automatically validate captcha code on blue event in default. And to check the UI captcha validation result, we listen the custom validatecaptcha event, which will be fired on captcha code input blur event.

Beside the captcha code input field, the example form contains three other fields such as name, email, subject, message. And in the React's componentDidMount method, we define validation functions and perform blur validation for these fields

On form submit (submitForm method), we need to send captcha id value and captcha code visitors submitted to server-side to validate Captcha code once at server-side api. Once request finished, we always reload Captcha by calling reloadImage() function of captcha object. This is needed to generate the new captcha code for the current captcha id.

II. Serverside

botdetect.xml

<?xml version="1.0" encoding="UTF-8"?>
<botdetect xmlns="https://captcha.com/schema/net"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="https://captcha.com/schema/net 
      https://captcha.com/schema/net/botdetect-4.4.0.xsd">

  <captchaStyles>
    <captchaStyle>
      <name>reactFormCaptcha</name>
      <userInputID>captchaCode</userInputID>
      <codeLength>4-6</codeLength>
      <codeStyle>Alphanumeric</codeStyle>
    </captchaStyle>
  </captchaStyles>
</botdetect>

In WEB-INF/botdetect.xml, we configure some captcha options for our jquery contact form captcha. You can find a full list of available Captcha configuration options and related instructions at the Captcha configuration options page.

ContactHandler.ashx

<%@ WebHandler Language="C#" Class="BasicHandler" %>

using System;
using System.Web;
using System.IO;
using Newtonsoft.Json;
using System.Collections.Generic;
using BotDetect.Web;


public class BasicHandler : IHttpHandler
{
    public void ProcessRequest (HttpContext context)
    {
        if (HttpContext.Current.Request.HttpMethod == "POST")
        {
            string dataJson = new StreamReader(context.Request.InputStream).ReadToEnd();

            Dictionary<string, string> formDataObj = new Dictionary<string, string>();
            formDataObj = JsonConvert.DeserializeObject<Dictionary<string, string>>(dataJson);

            string captchaId = formDataObj["captchaId"];
            string captchaCode = formDataObj["captchaCode"];

            // validate captcha
            SimpleCaptcha captcha = new SimpleCaptcha();
            bool isHuman = captcha.Validate(captchaCode, captchaId);

            if (isHuman)
            {
                // Captcha validation passed
                // TODO: do whatever you want here
            }

            // the object that stores validation result
            Dictionary<string, bool> validationResult = new Dictionary<string, bool>();
            validationResult.Add("success", isHuman);

            // write the validation result as json string for sending it back to client
            context.Response.ContentType = "application/json; charset=utf-8";
            context.Response.Write(JsonConvert.SerializeObject(validationResult));
        }
        else
        {
            context.Response.ContentType = "text/plain";
            context.Response.Write("HTTP GET request is not allowed.");
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

At server-side api, we will get captchaId and captchaCode values sent from client-side and use Validate(captchaCode, captchaId) method of SimpleCaptcha instance to validate Captcha code. Finally, we write the validation result as json string for sending it back to client.

Please Note

React Captcha Component requires the new experimental Simple API that is currently available in BotDetect Java v4.0.Beta3+, BotDetect PHP v4.2.0+, as well as in BotDetect ASP.NET v4.4.0 build for legacy .NET.
The Simple API for ASP.NET Core will be available in the BotDetect ASP.NET v4.4.1 that will be released in few weeks time.