Angular Form CAPTCHA Code Example

Please Note

Angular Captcha Module requires the new experimental Simple API that is currently available only in BotDetect Java version (4.0.Beta3+). Click here to find out when the Simple API will be available in BotDetect ASP.NET, PHP, and Classic ASP.

  1. Client-side
    1. AngularJS 1.x
    2. Angular 2+
  2. Server-side

I. Client-side

a) AngularJS 1.x

The AngularJS Form Captcha code example shows how to add BotDetect CAPTCHA protection to a typical AngularJS 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 Java CAPTCHA Library and run this example

Downloaded Location

The AngularJS Form CAPTCHA code example is included in the examples/simple-api/bdc4-simple-api-angularjs-captcha-example.war file of the download package. When deploying (unpacking) the file you will see the following main source code files:

contact-captcha.html

<form name="contactForm" novalidate ng-controller="ContactController" ng-submit="send(contactForm.$valid)" >
  
  <div class="alert alert-success" ng-show="successMessages">
    {{ successMessages }}
  </div>

  <div class="alert alert-error" ng-show="errorMessages">
    <ul class="list-messages" ng-repeat="(field, message) in errorMessages">
      <li>{{ message }}</li>
    </ul>
  </div>
  
  
  <label>
    <span>Name:</span>
    <input type="text" name="name" ng-model="name" ng-minlength="3">
  </label>
  
  <div class="error" ng-show="contactForm.name.$error.minlength && !contactForm.name.$pristine">
    Name must be at least 3 characters.
  </div>
  
  
  <label>
    <span>Email</span>
    <input type="email" name="email" ng-model="email" required>
  </label>
  
  <div class="error" ng-show="contactForm.email.$invalid && !contactForm.email.$pristine">
    Email is invalid.
  </div>
  

  <label>
    <span>Subject:</span>
    <input type="text" name="subject" ng-model="subject" ng-minlength="10">
  </label>
  
  <div class="error" ng-show="contactForm.subject.$error.minlength">
    Subject must be at least 10 characters.
  </div>
  
  
  <label>
    <span>Message:</span>
    <textarea name="message" ng-model="message" ng-minlength="10"></textarea>
  </label>
  
  <div class="error" ng-show="contactForm.message.$error.minlength">
    Message must be at least 10 characters.
  </div>
  
  
  <!-- show Captcha image html-->
  <botdetect-captcha styleName="angularFormCaptcha"></botdetect-captcha>
  
  <label>
    <span>Retype the characters from the picture:</span>
    <!-- captcha code user input textbox -->
    <input type="text" name="captchaCode" ng-model="captchaCode" correct-captcha id="captchaCode" autocomplete="off">
  </label>
  
  <div class="error" ng-show="contactForm.captchaCode.$invalid && !contactForm.captchaCode.$pristine">
    Incorrect code.
  </div>

  <button type="submit" ng-disabled="contactForm.$invalid" class="btn btn-primary">Send</button>
</form>

Adding Captcha protection to the AngularJS form is as simple as using botdetect-captcha element, which is provided by BotDetect Captcha AngularJS Module and set to styleName attribute a Captcha style name defined in botdetect.xml configuration file below.

We have used correct-captcha directive attribute in captcha code input field to perform UI Captcha validation on blur event.

To display captcha error message, we simply check $invalid of captchaCode. If it returns true, this means that UI captcha validation failed.

Beside the captcha code input field, the example form contains three other fields which are validated using AngularJS directive validation.

app.js

var app = angular.module('app', ['BotDetectCaptcha']);

app.config(function(captchaSettingsProvider) {
  captchaSettingsProvider.setSettings({
    captchaEndpoint: '/bdc4-simple-api-angularjs-captcha-example/botdetectcaptcha'
  });
});


app.controller('ContactController', function($scope, $http, Captcha) {
  // captcha validation messages
  $scope.successMessages = '';
  $scope.errorMessages = '';
  
  // contact url
  var contactUrl = '/bdc4-simple-api-angularjs-captcha-example/contact';

  $scope.send = function(isValidForm) {

    if (!isValidForm) {
      return;
    }
    
    // after UI form validation passed, 
    // we will need to validate captcha at server-side before we save form data in database, etc.

    // create new BotDetect Angular Captcha instance
    var captcha = new Captcha();
    
    // captcha id for validating captcha at server-side
    var captchaId = captcha.captchaId;
    
    // captcha code input value for validating captcha at server-side
    var captchaCode = $scope.captchaCode;
    
    var postData = {
      name: $scope.name,
      email: $scope.email,
      subject: $scope.subject,
      message: $scope.message,
      captchaId: captchaId,
      captchaCode: captchaCode
    };
    
    $http({
      method: 'POST',
      url: contactUrl,
      data: JSON.stringify(postData)
    })
      .then(function(response) {
        if (response.data.success) {
          // captcha, other form data passed and the data is also stored in database
          // show success message
          $scope.successMessages = 'Your message was sent successfully!';
          $scope.errorMessages = null;
        } else {
          // form validation failed
          $scope.errorMessages = response.data.errors;
          $scope.successMessages = null;
        }
        
        // always reload captcha image after validating captcha at server-side 
        // in order to update new captcha code for current captcha id
        captcha.reloadImage();
      }, function(error) {
        console.log(error.data);
      });
  };
  
});

First, we add BotDetectCaptcha to the app module as a dependency and configure BotDetect Java Captcha path to captchaEndpoint settings in config function.

In the code above, we inject Captcha into ContactController.

To validate Captcha code once at server-side api, we need to send captchaId property of Captcha object and captcha code visitors submitted to server-side.

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.

b) Angular 2+

The Angular Form Captcha code example shows how to add BotDetect CAPTCHA protection to a typical Angular 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 Java CAPTCHA Library and run this example

Downloaded Location

The Angular Form CAPTCHA code example is included in the examples/simple-api/bdc4-simple-api-angular-captcha-example.war file of the download package. When deploying (unpacking) the file you will see the following main source code files:

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppRoutingModule } from './app-routing.module';
import { BotDetectCaptchaModule } from 'angular-captcha';

import { AppComponent } from './app.component';
import { ContactComponent }   from './contact/contact.component';

import { ValuesPipe } from './values.pipe';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule,
    AppRoutingModule,
    BotDetectCaptchaModule.forRoot({
      captchaEndpoint: '/bdc4-simple-api-angular-captcha-example/botdetectcaptcha',
    })
  ],
  declarations: [
    AppComponent,
    ContactComponent,
    ValuesPipe
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

We import BotDetectCaptchaModule, then declare it in metadata imports of NgModule and configure BotDetect Java Captcha path in captchaEndpoint settings.

contact.component.html

<form novalidate (ngSubmit)="send(contact)" [formGroup]="contact">

  <div class="alert alert-success" *ngIf="successMessages">
    {{ successMessages }}
  </div>

  <div class="alert alert-error" *ngIf="errorMessages">
    <ul class="list-messages">
      <li *ngFor="let error of errorMessages | values">
        {{ error }}
      </li>
    </ul>
  </div>

  <label>
    <span>Name:</span>
    <input
      type="text"
      name="name"
      formControlName="name">
  </label>

  <div
    class="error"
    *ngIf="contact.get('name').hasError('minlength') && contact.get('name').touched"
    >
    Name must be at least 3 characters.
  </div>


  <label>
    <span>Email:</span>
    <input
      type="email"
      name="email"
      formControlName="email">
  </label>

  <div
    class="error"
    *ngIf="contact.get('email').hasError('pattern') && contact.get('email').touched"
    >
    Email is invalid.
  </div>


  <label>
    <span>Subject:</span>
    <input
      type="text"
      name="subject"
      formControlName="subject">
  </label>

  <div
    class="error"
    *ngIf="contact.get('subject').hasError('minlength') && contact.get('subject').touched"
    >
    Subject must be at least 10 characters.
  </div>


  <label>
    <span>Message:</span>
    <textarea
      name="message"
      formControlName="message"></textarea>
  </label>

  <div
    class="error textarea-error"
    *ngIf="contact.get('message').hasError('minlength') && contact.get('message').touched"
    >
    Message must be at least 10 characters.
  </div>
  
  <!-- show captcha html -->
  <botdetect-captcha styleName="angularFormCaptcha"></botdetect-captcha>

  <label>
    <span>Retype the characters from the picture:</span>
    <input
      type="text"
      id="captchaCode"
      name="captchaCode"
      formControlName="captchaCode"
      correctCaptcha
      >
  </label>

  <div
    class="error"
    *ngIf="contact.get('captchaCode').hasError('incorrectCaptcha') && !contact.get('captchaCode').pristine"
    >
    Incorrect code.
  </div>

  <button type="submit" [disabled]="contact.invalid">Send</button>
</form>

Adding Captcha protection to the Angular form is as simple as using botdetect-captcha element, which is provided by BotDetect Captcha Angular Module and set to styleName attribute a Captcha style name defined in botdetect.xml configuration file below.

we have used correctCaptcha directive attribute in captcha code input field to perform UI Captcha validation on blur event.

To display captcha error message, you simply check if incorrectCaptcha property exists in captchaCode.errors object. If it exists, this means that UI captcha validation failed.

Beside the captcha code input field, the example form contains three other fields which are going to be validated in contact.component.ts later.

contact.component.ts

import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Observable } from 'rxjs/Rx';

import { CaptchaComponent } from 'angular-captcha';

import { Contact } from './contact.interface';
import { ContactService } from './contact.service';

@Component({
  moduleId: module.id,
  selector: 'contact-form',
  templateUrl: 'contact.component.html',
  styleUrls: ['contact.component.css'],
  providers: [ContactService]
})
export class ContactComponent implements OnInit {
  
  contact: FormGroup;

  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,}))$/;

  /**
   * Captcha validation messages.
   */
  errorMessages: Object;
  successMessages: string;

  /**
   * BotDetect CAPTCHA component.
   */
  @ViewChild(CaptchaComponent) captchaComponent: CaptchaComponent;

  constructor(
    private fb: FormBuilder,
    private contactService: ContactService
  ) { }

  ngOnInit(): void {
    this.contact = this.fb.group({
      name: ['', Validators.minLength(3)],
      email: ['', Validators.pattern(this.emailRegex)],
      subject: ['', Validators.minLength(10)],
      message: ['', Validators.minLength(10)],
      captchaCode: [''] // we use 'correctCaptcha' directive to validate captcha code control in the template
    });
  }

  send({ value, valid }: { value: Contact, valid: boolean }): void {
    if (!valid) {
      return;
    }

    let postData = value;
    // add captcha captcha id to postData for validating captcha at server-side
    postData['captchaId'] = this.captchaComponent.captchaId;

    this.contactService.send(postData)
      .subscribe(
        response => {
          if (response.success) {
            // captcha validation passed at server-side
            this.successMessages = 'CAPTCHA validation passed.';
            this.errorMessages = null;
          } else {
            // captcha validation falied at server-side
            this.errorMessages = response.errors;
            this.successMessages = '';
          }

          // always reload captcha image after validating captcha at server-side 
          // in order to update new captcha code for current captcha id
          this.captchaComponent.reloadImage();
        },
        error => {
          throw new Error(error);
        });
  }
}

In the code above, we inject CaptchaComponent into BasicComponent using @ViewChild.

To validate Captcha code once at server-side api, we need to send captchaId property of CaptchaComponent and captcha code visitors submitted to server-side.

Once request finished, we always reload Captcha by calling the reloadImage() function of CaptchaComponent. This is needed to generate the new captcha code for the current captcha id.

contact.service.ts

import { Injectable }    from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';

import { Observable } from 'rxjs/Rx';

@Injectable()
export class ContactService {

  // contact api url
  contactUrl = '/bdc4-simple-api-angular-captcha-example/contact';

  constructor(private http: Http) { }

  send(data: Object): Observable<any> {
    const headers = new Headers({ 'Content-Type': 'application/json' });
    const options = new RequestOptions({ headers: headers });

    return this.http.post(this.contactUrl, data, options)
      .map((response: Response) => response.json())
      .catch((error:any) => Observable.throw(error.json().error));
  }
}

In this ContactService, we simply add a function that posts data to the contact api url using Http post of HttpModule.

contact.interface.ts

export interface Contact {
  name: string;
  email: string;
  subject: string;
  message: string;
  captchaCode: string;
}

We implement this interface to use in contact.component.ts.


II. Server-side

botdetect.xml

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

  <captchaStyles>
    <captchaStyle>
      <name>angularFormCaptcha</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 angularjs basic captcha. You can find a full list of available Captcha configuration options and related instructions at the Captcha configuration options page.

ContactServlet.java

package com.captcha.botdetect.examples.angular.contact_form;

import com.captcha.botdetect.web.servlet.SimpleCaptcha;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ContactServlet extends HttpServlet {

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    
    PrintWriter out = response.getWriter();
    Gson gson = new Gson();
    Map<String, String> errors = new HashMap<String, String>();
    
    response.setContentType("application/json; charset=utf-8");
    
    JsonParser parser = new JsonParser();
    JsonObject formDataObj = (JsonObject) parser.parse(request.getReader());
    
    String name = formDataObj.get("name").getAsString();
    String email = formDataObj.get("email").getAsString();
    String subject = formDataObj.get("subject").getAsString();
    String message = formDataObj.get("message").getAsString();
    String captchaId = formDataObj.get("captchaId").getAsString();
    String captchaCode = formDataObj.get("captchaCode").getAsString();
     
    if (!isValidName(name)) {
      errors.put("name", "Name must be at least 3 characters.");
    }
    
    if (!isValidEmail(email)) {
      errors.put("email", "Email is invalid.");
    }
    
    if (!isValidSubject(subject)) {
      errors.put("message", "Subject must be at least 10 characters.");
    }
    
    if (!isValidMessage(message)) {
      errors.put("message", "Message must be at least 10 characters.");
    }
    
    if (!isCaptchaCorrect(request, captchaCode, captchaId)) {
      errors.put("captchaCode", "CAPTCHA validation failed.");
    }
    
    if (errors.isEmpty()) {
      // everything is ok
      // TODO: Insert form data into your database
    }
    
    // the object that stores validation result
    ContactValidationResult validationResult = new ContactValidationResult();
    validationResult.setSuccess(errors.isEmpty());
    validationResult.setErrors(errors);
    
    try {
      // write the validation result as json string for sending it back to client
      out.write(gson.toJson(validationResult));
    } finally {
      out.close();
    }
  }
  
 
  private boolean isCaptchaCorrect(HttpServletRequest request, String captchaCode, String captchaId) {
    SimpleCaptcha captcha = SimpleCaptcha.load(request);
    return captcha.validate(captchaCode, captchaId);
  }
  
  private boolean isValidName(String name) {
    if (name == null) {
      return false;
    }
    return (name.length() >= 3);
  }
  
  private boolean isValidEmail(String email) {
    if (email == null) {
      return false;
    }
    return email.matches("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$");
  }
  
  private boolean isValidSubject(String subject) {
    if (subject == null) {
      return false;
    }
    return (subject.length() > 9) && (subject.length() < 255);
  }
  
  private boolean isValidMessage(String message) {
    if (message == null) {
      return false;
    }
    return (message.length() > 9) && (message.length() < 255);
  }
}

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.

ContactValidationResult.java

package com.captcha.botdetect.examples.angular.contact_form;

import java.util.HashMap;
import java.util.Map;

public class ContactValidationResult {
  private boolean success;
  private Map<String, String> errors;

  public ContactValidationResult() {
    errors = new HashMap<String, String>();
  }

  public boolean getSuccess() {
    return success;
  }

  public void setSuccess(boolean success) {
    this.success = success;
  }

  public Map<String, String> getErrors() {
    return errors;
  }

  public void setErrors(Map<String, String> errors) {
    this.errors = errors;
  }
}

In this example, we just use this class to store Captcha validation result and use it to convert to JSON string using Gson library in ContactServlet.