Angular & AngularJS Modules: The Contact Form Code Example

Click on the link below that matches the Angular version that you are using:

..............................................................

Angular 2/4/5/6/7/8-based Frontend

Click on the link below that matches your BotDetect CAPTCHA Angular Module usage scenario:

First Time Here?

The Angular CAPTCHA Integration Guide is the best place to start!

..............................................................

Angular-based Frontend and the web API with MVC Core 1/2 on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first Angular captcha integration by following our step-by-step Angular CAPTCHA Integration Guide.

Introduction

This contact form Angular captcha example shows the code required to display and validate captchas in a contact form of the application with a Angular frontend and a web API with MVC Core 1/2 backend.

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

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

This example is in the <BDC-DIR>/core-on-core/examples/s_api-captcha-angular-webapi_mvccore2/ folder; and contains the following files:

Frontend: app.module.ts

The angular-src/app/app.module.ts source file does the following:

  • Loads the BotDetect CAPTCHA Angular Module into the page
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
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 { BasicComponent }   from './basic/basic.component';
import { ContactComponent }   from './contact/contact.component';

import { BasicSuccessNotifyComponent }   from './notify/basic-notify/basic-success-notify.component';
import { ContactSuccessNotifyComponent }   from './notify/contact-notify/contact-success-notify.component';

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

@NgModule({
  declarations: [
    AppComponent,
    BasicComponent,
    ContactComponent,
    ValuesPipe,
    BasicSuccessNotifyComponent,
    ContactSuccessNotifyComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule,
    AppRoutingModule,
    // import Captcha Angular Module
    BotDetectCaptchaModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Frontend: contact.component.html

The angular-src/app/contact/contact.component.html source file contains the following:

  • Displays the captcha and user-input markups on the page
<form novalidate (ngSubmit)="send(contact)" [formGroup]="contact">

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>

  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input
      type="text"
      id="userCaptchaInput"
      name="userCaptchaInput"
      formControlName="userCaptchaInput"
    >
  </label>

  <button type="submit">Send</button>
</form>

Frontend: contact.component.ts

The angular-src/app/contact/contact.component.ts source file does the following:

  • Loads the captcha challenges from backend
  • Sets the captchaEndpoint property to point to the captcha endpoint path on backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
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.
   */
  
  // if you use Angular 2/4/5/6/7 use this line:
  @ViewChild(CaptchaComponent) captchaComponent: CaptchaComponent;

  // if you use Angular 8 use this line instead of the above one:
  // @ViewChild(CaptchaComponent, { static: true }) captchaComponent: CaptchaComponent;  

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

  ngOnInit(): void {
    this.contact = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['',  [Validators.required, Validators.pattern(this.emailRegex)]],
      subject: ['',  [Validators.required,Validators.minLength(10)]],
      message: ['',  [Validators.required,Validators.minLength(10)]],
      userCaptchaInput: [''] // we will validate user captcha input value at the backend side
    });

    // set the captchaEndpoint property to point to 
    // the captcha endpoint path on your app's backend
    this.captchaComponent.captchaEndpoint = 'simple-captcha-endpoint.ashx';
  }

  /**
   * Process the contact form on submit event.
   */
  send({ value, valid }: { value: Contact, valid: boolean }): void {

    if (valid) {

      // get the user-entered captcha code value to be validated at the backend side        
      let userEnteredCaptchaCode = this.captchaComponent.userEnteredCaptchaCode;
      
      // get the id of a captcha instance that the user tried to solve
      let captchaId = this.captchaComponent.captchaId;

      let postData = {
        name: value.name,
        email: value.email,
        subject: value.subject,
        message: value.message,
        // add the user-entered captcha code value to the post data    
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data      
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      this.contactService.send(postData)
        .subscribe(
          response => {
            if (response.success == false) {
              // captcha validation failed; show the error message
              this.errorMessages = response.errors;
              // call the this.captchaComponent.reloadImage()
              // in order to generate a new captcha challenge
              this.captchaComponent.reloadImage();
            } else {
              // captcha validation succeeded; proceed with the workflow
              this.router.navigate(['/contact-success-notify']) 
            }
          },
          error => {
            throw new Error(error);
          });

    } else {
      this.errorMessages = { formInvalid: 'Please enter valid values.' }
    }

  }
}

Frontend: contact.service.ts

The angular-src/app/contact/contact.service.ts source file does the following:

  • Sends the post request with the captcha id and the user entered captcha code to backend
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 = 'api/contact-captcha';

  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));
  }
}

Backend: appsettings.json

The appsettings.json source file contains the following:

  • BotDetect configuration section that defines the CaptchaEndpointPath path
{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "BotDetect": {
    "CaptchaEndpointPath": "simple-captcha-endpoint.ashx"
  }
}

Backend: Startup.cs

The Startup.cs source file does the following:

  • Configures the application pipeline to use SimpleCaptcha middleware
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using BotDetect.Web;
using BotDetect.Web.Http;

namespace AngularWebAPIwithMVC6CaptchaExample
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // You can omit the next line on .NET Core 2.0 or higher
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddMemoryCache(); // Adds a default in-memory 
                                       // implementation of 
                                       // IDistributedCache

            // Add framework services.
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseDefaultFiles(options);
            app.UseStaticFiles();

            // Configure the application pipeline to use SimpleCaptcha middleware
            app.UseSimpleCaptcha(Configuration.GetSection("BotDetect"));

            app.UseMvc();
        }
    }
}

Backend: botdetect.xml

The botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.2.xsd">

  <captchaStyles>
    <captchaStyle>
      <name>angularFormCaptcha</name>
      <userInputID>userCaptchaInput</userInputID>
      <codeLength>4-6</codeLength>
    </captchaStyle>
  </captchaStyles>
</botdetect>

Backend: ContactApiController.cs

The Controllers/ContactApiController.cs source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the Validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using BotDetect.Web;

namespace AngularWebAPIwithMVC6CaptchaExample.Controllers
{
    [Route("api/contact-captcha")]
    public class ContactApiController : Controller
    {
        [HttpPost]
        public IActionResult Post([FromBody] Models.ContactFormModel data)
        {
            string name = data.Name;
            string email = data.Email;
            string subject = data.Subject;
            string message = data.Message;

            string userEnteredCaptchaCode = data.UserEnteredCaptchaCode;
            string captchaId = data.CaptchaId;

            // validate the form data
            Dictionary<string, string> errors = new Dictionary<string, string>();

            if (!IsValidName(name))
            {
                errors.Add("name", "Name must be at least 3 chars long!");
            }

            if (!IsValidEmail(email))
            {
                errors.Add("email", "Email is invalid!");
            }

            if (!IsValidSubject(subject))
            {
                errors.Add("subject", "Subject must be at least 10 chars long!");
            }

            if (!IsValidMessage(message))
            {
                errors.Add("message", "Message must be at least 10 chars long!");
            }

            // validate the user entered captcha code
            if (!IsCaptchaCorrect(userEnteredCaptchaCode, captchaId))
            {
                errors.Add("userEnteredCaptchaCode", "CAPTCHA validation failed!");
                // TODO: consider logging the attempt
            }

            bool isErrorsEmpty = errors.Count > 0 ? false : true;

            if (isErrorsEmpty)
            {
                // TODO: all validations succeeded; execute the protected action
                // (send email, write to database, etc...)
            }

            // create an object that stores the validation result
            Dictionary<string, object> validationResult = new Dictionary<string, object>();
            validationResult.Add("success", isErrorsEmpty);
            validationResult.Add("errors", errors);

            // return the json string with the validation result to the frontend
            return Json(validationResult);
        }


        // the captcha validation function
        private bool IsCaptchaCorrect(string userEnteredCaptchaCode, string captchaId)
        {
            // create a captcha instance to be used for the captcha validation
            SimpleCaptcha captcha = new SimpleCaptcha();
            // execute the captcha validation
            return captcha.Validate(userEnteredCaptchaCode, captchaId);
        }

        private bool IsValidName(string name)
        {
            if (name == null)
            {
                return false;
            }

            return (name.Length >= 3);
        }

        private bool IsValidEmail(string email)
        {
            if (email == null)
            {
                return false;
            }
            Regex regex = new Regex(@"^[\w-_\.+]*[\w-_\.]\@([\w]+\.)+[\w]+[\w]$");
            Match match = regex.Match(email);

            return match.Success;
        }

        private bool IsValidSubject(string subject)
        {
            if (subject == null)
            {
                return false;
            }

            return (subject.Length > 9) && (subject.Length < 255);
        }

        private bool IsValidMessage(string message)
        {
            if (message == null)
            {
                return false;
            }

            return (message.Length > 9) && (message.Length < 255);
        }
    }
}

..............................................................

Angular-based Frontend and the Legacy ASP.NET Web-API 2 on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first Angular captcha integration by following our step-by-step Angular CAPTCHA Integration Guide.

Introduction

This contact form Angular captcha example shows the code required to display and validate captchas in a contact form of the application with a Angular frontend and a legacy ASP.NET Web-API 2 backend.

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

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/s_api-captcha-angular-webapi2/csharp/ folder; and contains the following files:

Frontend: app.module.ts

The angular-src/app/app.module.ts source file does the following:

  • Loads the BotDetect CAPTCHA Angular Module into the page
  • Sets the captchaEndpoint property to point to the captcha endpoint path on backend
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
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 { BasicComponent }   from './basic/basic.component';
import { ContactComponent }   from './contact/contact.component';

import { BasicSuccessNotifyComponent }   from './notify/basic-notify/basic-success-notify.component';
import { ContactSuccessNotifyComponent }   from './notify/contact-notify/contact-success-notify.component';

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

@NgModule({
  declarations: [
    AppComponent,
    BasicComponent,
    ContactComponent,
    ValuesPipe,
    BasicSuccessNotifyComponent,
    ContactSuccessNotifyComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule,
    AppRoutingModule,
    // import Captcha Angular Module
    BotDetectCaptchaModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Frontend: contact.component.html

The angular-src/app/contact/contact.component.html source file contains the following:

  • Displays the captcha and user-input markups on the page
<form novalidate (ngSubmit)="send(contact)" [formGroup]="contact">

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>

  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input
      type="text"
      id="userCaptchaInput"
      name="userCaptchaInput"
      formControlName="userCaptchaInput"
    >
  </label>

  <button type="submit">Send</button>
</form>

Frontend: contact.component.ts

The angular-src/app/contact/contact.component.ts source file does the following:

  • Loads the captcha challenges from backend
  • Sets the captchaEndpoint property to point to the captcha endpoint path on backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
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.
   */
  
  // if you use Angular 2/4/5/6/7 use this line:
  @ViewChild(CaptchaComponent) captchaComponent: CaptchaComponent;

  // if you use Angular 8 use this line instead of the above one:
  // @ViewChild(CaptchaComponent, { static: true }) captchaComponent: CaptchaComponent;  

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

  ngOnInit(): void {
    this.contact = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['',  [Validators.required, Validators.pattern(this.emailRegex)]],
      subject: ['',  [Validators.required,Validators.minLength(10)]],
      message: ['',  [Validators.required,Validators.minLength(10)]],
      userCaptchaInput: [''] // we will validate user captcha input value at the backend side
    });

    // set the captchaEndpoint property to point to 
    // the captcha endpoint path on your app's backend
    this.captchaComponent.captchaEndpoint = 'simple-captcha-endpoint.ashx';
  }

  /**
   * Process the contact form on submit event.
   */
  send({ value, valid }: { value: Contact, valid: boolean }): void {

    if (valid) {

      // get the user-entered captcha code value to be validated at the backend side        
      let userEnteredCaptchaCode = this.captchaComponent.userEnteredCaptchaCode;
      
      // get the id of a captcha instance that the user tried to solve
      let captchaId = this.captchaComponent.captchaId;

      let postData = {
        name: value.name,
        email: value.email,
        subject: value.subject,
        message: value.message,
        // add the user-entered captcha code value to the post data    
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data      
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      this.contactService.send(postData)
        .subscribe(
          response => {
            if (response.success == false) {
              // captcha validation failed; show the error message
              this.errorMessages = response.errors;
              // call the this.captchaComponent.reloadImage()
              // in order to generate a new captcha challenge
              this.captchaComponent.reloadImage();
            } else {
              // captcha validation succeeded; proceed with the workflow
              this.router.navigate(['/contact-success-notify']) 
            }
          },
          error => {
            throw new Error(error);
          });

    } else {
      this.errorMessages = { formInvalid: 'Please enter valid values.' }
    }

  }
}

Frontend: contact.service.ts

The angular-src/app/contact/contact.service.ts source file does the following:

  • Sends the post request with the captcha id and the user entered captcha code to backend
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 = 'api/webapi/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));
  }
}

Backend: web.config

The web.config source file does the following:

  • Registers the simple-captcha-endpoint.ashx path
  • Maps it to the BotDetect.Web.SimpleCaptchaHandler
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings></appSettings>
  <system.web>
    <compilation debug="false" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="simple-captcha-endpoint.ashx"
	   type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </httpHandlers>
  </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="simple-captcha-endpoint.ashx"
	   type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
</configuration>

Backend: botdetect.xml

The botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.2.xsd">

    <captchaStyles>
        <captchaStyle>
            <name>angularFormCaptcha</name>
            <userInputID>userCaptchaInput</userInputID>
            <codeLength>4-6</codeLength>
        </captchaStyle>
    </captchaStyles>
</botdetect>

Backend: WebApiController.cs

The Backend/Controllers/WebApiController.cs source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the Validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Http;
using AngularWebAPICaptchaExampleCSharp.Backend.Models;

using BotDetect.Web;

namespace AngularWebAPICaptchaExampleCSharp.Backend.Controllers
{
    public class WebApiController : ApiController
    {

        // POST api/webapi/contact
        public IHttpActionResult Contact([FromBody]CaptchaContactModel data)
        {
            string name = data.Name;
            string email = data.Email;
            string subject = data.Subject;
            string message = data.Message;

            string userEnteredCaptchaCode = data.UserEnteredCaptchaCode;
            string captchaId = data.CaptchaId;

            // validate the form data
            Dictionary<string, string> errors = new Dictionary<string, string>();

            if (!IsValidName(name))
            {
                errors.Add("name", "Name must be at least 3 chars long!");
            }

            if (!IsValidEmail(email))
            {
                errors.Add("email", "Email is invalid!");
            }

            if (!IsValidSubject(subject))
            {
                errors.Add("subject", "Subject must be at least 10 chars long!");
            }

            if (!IsValidMessage(message))
            {
                errors.Add("message", "Message must be at least 10 chars long!");
            }

            // validate the user entered captcha code
            if (!IsCaptchaCorrect(userEnteredCaptchaCode, captchaId))
            {
                errors.Add("userEnteredCaptchaCode", "CAPTCHA validation failed!");
                // TODO: consider logging the attempt
            }

            bool isErrorsEmpty = errors.Count <= 0;

            if (isErrorsEmpty)
            {
                // TODO: all validations succeeded; execute the protected action
                // (send email, write to database, etc...)
            }

            // create an object that stores the validation result
            Dictionary<string, object> validationResult = new Dictionary<string, object>();
            validationResult.Add("success", isErrorsEmpty);
            validationResult.Add("errors", errors);

            // return the json string with the validation result to the frontend
            return Ok(validationResult);
        }

        // the captcha validation function
        private bool IsCaptchaCorrect(string userEnteredCaptchaCode, string captchaId)
        {
            // create a captcha instance to be used for the captcha validation
            SimpleCaptcha captcha = new SimpleCaptcha();
            // execute the captcha validation
            return captcha.Validate(userEnteredCaptchaCode, captchaId);
        }

        private bool IsValidName(string name)
        {
            if (name == null)
            {
                return false;
            }

            return (name.Length >= 3);
        }

        private bool IsValidEmail(string email)
        {
            if (email == null)
            {
                return false;
            }
            Regex regex = new Regex("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$");
            Match match = regex.Match(email);

            return match.Success;
        }

        private bool IsValidSubject(string subject)
        {
            if (subject == null)
            {
                return false;
            }

            return (subject.Length > 9) && (subject.Length < 255);
        }

        private bool IsValidMessage(string message)
        {
            if (message == null)
            {
                return false;
            }

            return (message.Length > 9) && (message.Length < 255);
        }

    }

    public class CaptchaContactModel
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("email")]
        public string Email { get; set; }

        [JsonProperty("subject")]
        public string Subject { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }

        [JsonProperty("captchaId")]
        public string CaptchaId { get; set; }

        [JsonProperty("userEnteredCaptchaCode")]
        public string UserEnteredCaptchaCode { get; set; }
    }

}

..............................................................

Angular-based Frontend and the Legacy ASP.NET Generic Handler on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first Angular captcha integration by following our step-by-step Angular CAPTCHA Integration Guide.

Introduction

This contact form Angular captcha example shows the code required to display and validate captchas in a contact form of the application with a Angular frontend and a legacy ASP.NET Generic Handler backend.

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

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/s_api-captcha-angular-generic_handler/csharp/ folder; and contains the following files:

Frontend: app.module.ts

The angular-src/app/app.module.ts source file does the following:

  • Loads the BotDetect CAPTCHA Angular Module into the page
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
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 { BasicComponent }   from './basic/basic.component';
import { ContactComponent }   from './contact/contact.component';

import { BasicSuccessNotifyComponent }   from './notify/basic-notify/basic-success-notify.component';
import { ContactSuccessNotifyComponent }   from './notify/contact-notify/contact-success-notify.component';

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

@NgModule({
  declarations: [
    AppComponent,
    BasicComponent,
    ContactComponent,
    ValuesPipe,
    BasicSuccessNotifyComponent,
    ContactSuccessNotifyComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule,
    AppRoutingModule,
    // import Captcha Angular Module
    BotDetectCaptchaModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Frontend: contact.component.html

The angular-src/app/contact/contact.component.html source file contains the following:

  • Displays the captcha and user-input markups on the page
<form novalidate (ngSubmit)="send(contact)" [formGroup]="contact">

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>

  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input
      type="text"
      id="userCaptchaInput"
      name="userCaptchaInput"
      formControlName="userCaptchaInput"
    >
  </label>

  <button type="submit">Send</button>
</form>

Frontend: contact.component.ts

The angular-src/app/contact/contact.component.ts source file does the following:

  • Loads the captcha challenges from backend
  • Sets the captchaEndpoint property to point to the captcha endpoint path on backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
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.
   */
  
  // if you use Angular 2/4/5/6/7 use this line:
  @ViewChild(CaptchaComponent) captchaComponent: CaptchaComponent;

  // if you use Angular 8 use this line instead of the above one:
  // @ViewChild(CaptchaComponent, { static: true }) captchaComponent: CaptchaComponent;  

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

  ngOnInit(): void {
    this.contact = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['',  [Validators.required, Validators.pattern(this.emailRegex)]],
      subject: ['',  [Validators.required,Validators.minLength(10)]],
      message: ['',  [Validators.required,Validators.minLength(10)]],
      userCaptchaInput: [''] // we will validate user captcha input value at the backend side
    });

    // set the captchaEndpoint property to point to 
    // the captcha endpoint path on your app's backend
    this.captchaComponent.captchaEndpoint = 'simple-captcha-endpoint.ashx';
  }

  /**
   * Process the contact form on submit event.
   */
  send({ value, valid }: { value: Contact, valid: boolean }): void {

    if (valid) {

      // get the user-entered captcha code value to be validated at the backend side        
      let userEnteredCaptchaCode = this.captchaComponent.userEnteredCaptchaCode;
      
      // get the id of a captcha instance that the user tried to solve
      let captchaId = this.captchaComponent.captchaId;

      let postData = {
        name: value.name,
        email: value.email,
        subject: value.subject,
        message: value.message,
        // add the user-entered captcha code value to the post data    
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data      
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      this.contactService.send(postData)
        .subscribe(
          response => {
            if (response.success == false) {
              // captcha validation failed; show the error message
              this.errorMessages = response.errors;
              // call the this.captchaComponent.reloadImage()
              // in order to generate a new captcha challenge
              this.captchaComponent.reloadImage();
            } else {
              // captcha validation succeeded; proceed with the workflow
              this.router.navigate(['/contact-success-notify']) 
            }
          },
          error => {
            throw new Error(error);
          });

    } else {
      this.errorMessages = { formInvalid: 'Please enter valid values.' }
    }

  }
}

Frontend: contact.service.ts

The angular-src/app/contact/contact.service.ts source file does the following:

  • Sends the post request with the captcha id and the user entered captcha code to backend
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 = 'ContactHandler.ashx';

  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));
  }
}

Backend: web.config

The web.config source file does the following:

  • Registers the simple-captcha-endpoint.ashx path
  • Maps it to the BotDetect.Web.SimpleCaptchaHandler
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="simple-captcha-endpoint.ashx"
           type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </httpHandlers>
  </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="simple-captcha-endpoint.ashx"
           type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
</configuration>

Backend: botdetect.xml

The botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.2.xsd">

    <captchaStyles>
        <captchaStyle>
            <name>angularFormCaptcha</name>
            <userInputID>userCaptchaInput</userInputID>
            <codeLength>4-6</codeLength>
        </captchaStyle>
    </captchaStyles>
</botdetect>

Backend: ContactHandler.ashx

The ContactHandler.ashx source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the Validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
<%@ WebHandler Language="C#" Class="ContactHandler" %>

using System;
using System.Web;
using System.IO;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using BotDetect.Web;

public class ContactHandler : 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 name = formDataObj["name"];
            string email = formDataObj["email"];
            string subject = formDataObj["subject"];
            string message = formDataObj["message"];

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

            // validate the form data
            Dictionary<string, string> errors = new Dictionary<string, string>();

            if (!IsValidName(name))
            {
                errors.Add("name", "Name must be at least 3 chars long!");
            }

            if (!IsValidEmail(email))
            {
                errors.Add("email", "Email is invalid!");
            }

            if (!IsValidSubject(subject))
            {
                errors.Add("subject", "Subject must be at least 10 chars long!");
            }

            if (!IsValidMessage(message))
            {
                errors.Add("message", "Message must be at least 10 chars long!");
            }

            // validate the user entered captcha code
            if (!IsCaptchaCorrect(userEnteredCaptchaCode, captchaId))
            {
                errors.Add("userEnteredCaptchaCode", "CAPTCHA validation failed!");
                // TODO: consider logging the attempt
            }

            bool isErrorsEmpty = (errors.Count > 0) ? false : true;

            if (isErrorsEmpty)
            {
                // TODO: all validations succeeded; execute the protected action
                // (send email, write to database, etc...)
            }

            // create an object that stores the validation result
            Dictionary<string, object> validationResult = new Dictionary<string, object>();
            validationResult.Add("success", isErrorsEmpty);
            validationResult.Add("errors", errors);

            // return the json string with the validation result to the frontend
            context.Response.ContentType = "application/json; charset=utf-8";
            context.Response.Write(JsonConvert.SerializeObject(validationResult));
        }
        else
        {
            context.Response.ContentType = "text/plain";
            context.Response.Write("Only HTTP POST requests are allowed.");
        }
    }

    // the captcha validation function
    private bool IsCaptchaCorrect(string userEnteredCaptchaCode, string captchaId)
    {
        // create a captcha instance to be used for the captcha validation
        SimpleCaptcha captcha = new SimpleCaptcha();
        // execute the captcha validation
        return captcha.Validate(userEnteredCaptchaCode, captchaId);
    }

    private bool IsValidName(string name)
    {
        if (name == null)
        {
            return false;
        }

        return (name.Length >= 3);
    }

    private bool IsValidEmail(string email)
    {
        if (email == null)
        {
            return false;
        }
        Regex regex = new Regex("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$");
        Match match = regex.Match(email);

        return match.Success;
    }

    private bool IsValidSubject(string subject)
    {
        if (subject == null)
        {
            return false;
        }

        return (subject.Length > 9) && (subject.Length < 255);
    }

    private bool IsValidMessage(string message)
    {
        if (message == null)
        {
            return false;
        }

        return (message.Length > 9) && (message.Length < 255);
    }

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

..............................................................

Angular-based Frontend and the Java Servlet on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first Angular captcha integration by following our step-by-step Angular CAPTCHA Integration Guide.

Introduction

This contact form Angular captcha example shows the code required to display and validate captchas in a contact form of the application with a Angular frontend and a Java Servlet backend.

Download the BotDetect Java CAPTCHA Library archive and run this example

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

This example is in the <BDC-DIR>/examples/s_api-captcha-angular-servlet/ folder; and contains the following files:

Frontend: app.module.ts

The src/main/webapp/angular-src/app/app.module.ts source file does the following:

  • Loads the BotDetect CAPTCHA Angular Module into the page
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
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 { BasicComponent }   from './basic/basic.component';
import { ContactComponent }   from './contact/contact.component';

import { BasicSuccessNotifyComponent }   from './notify/basic-notify/basic-success-notify.component';
import { ContactSuccessNotifyComponent }   from './notify/contact-notify/contact-success-notify.component';

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

@NgModule({
  declarations: [
    AppComponent,
    BasicComponent,
    ContactComponent,
    ValuesPipe,
    BasicSuccessNotifyComponent,
    ContactSuccessNotifyComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule,
    AppRoutingModule,
    // import Captcha Angular Module
    BotDetectCaptchaModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Frontend: contact.component.html

The src/main/webapp/angular-src/app/contact/contact.component.html source file contains the following:

  • Displays the captcha and user-input markups on the page
<form novalidate (ngSubmit)="send(contact)" [formGroup]="contact">

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>

  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input
      type="text"
      id="userCaptchaInput"
      name="userCaptchaInput"
      formControlName="userCaptchaInput"
    >
  </label>

  <button type="submit">Send</button>
</form>

Frontend: contact.component.ts

The src/main/webapp/angular-src/app/contact/contact.component.ts source file does the following:

  • Loads the captcha challenges from backend
  • Sets the captchaEndpoint property to point to the captcha endpoint path on backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
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.
   */
  
  // if you use Angular 2/4/5/6/7 use this line:
  @ViewChild(CaptchaComponent) captchaComponent: CaptchaComponent;

  // if you use Angular 8 use this line instead of the above one:
  // @ViewChild(CaptchaComponent, { static: true }) captchaComponent: CaptchaComponent;  

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

  ngOnInit(): void {
    this.contact = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['',  [Validators.required, Validators.pattern(this.emailRegex)]],
      subject: ['',  [Validators.required,Validators.minLength(10)]],
      message: ['',  [Validators.required,Validators.minLength(10)]],
      userCaptchaInput: [''] // we will validate user captcha input value at the backend side
    });

    // set the captchaEndpoint property to point to 
    // the captcha endpoint path on your app's backend
    this.captchaComponent.captchaEndpoint = 'simple-captcha-endpoint';
  }

  /**
   * Process the contact form on submit event.
   */
  send({ value, valid }: { value: Contact, valid: boolean }): void {

    if (valid) {

      // get the user-entered captcha code value to be validated at the backend side        
      let userEnteredCaptchaCode = this.captchaComponent.userEnteredCaptchaCode;
      
      // get the id of a captcha instance that the user tried to solve
      let captchaId = this.captchaComponent.captchaId;

      let postData = {
        name: value.name,
        email: value.email,
        subject: value.subject,
        message: value.message,
        // add the user-entered captcha code value to the post data    
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data      
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      this.contactService.send(postData)
        .subscribe(
          response => {
            if (response.success == false) {
              // captcha validation failed; show the error message
              this.errorMessages = response.errors;
              // call the this.captchaComponent.reloadImage()
              // in order to generate a new captcha challenge
              this.captchaComponent.reloadImage();
            } else {
              // captcha validation succeeded; proceed with the workflow
              this.router.navigate(['/contact-success-notify']) 
            }
          },
          error => {
            throw new Error(error);
          });

    } else {
      this.errorMessages = { formInvalid: 'Please enter valid values.' }
    }

  }
}

Frontend: contact.service.ts

The src/main/webapp/angular-src/app/contact/contact.service.ts source file does the following:

  • Sends the post request with the captcha id and the user entered captcha code to backend
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 = '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));
  }
}

Backend: web.xml

The src/main/webapp/WEB-INF/web.xml source file does the following:

  • Registers the simple-captcha-endpoint path
  • Maps it to the com.captcha.botdetect.web.servlet.SimpleCaptchaServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee                 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <servlet>
        <servlet-name>BotDetect Captcha</servlet-name>
        <servlet-class>com.captcha.botdetect.web.servlet.SimpleCaptchaServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BotDetect Captcha</servlet-name>
        <url-pattern>/simple-captcha-endpoint</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>Contact Captcha</servlet-name>
        <servlet-class>com.captcha.botdetect.examples.angular.contact_form.ContactServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Contact Captcha</servlet-name>
        <url-pattern>/contact</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

Backend: botdetect.xml

The src/main/webapp/WEB-INF/botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.7.xsd">

    <captchaStyles>
        <captchaStyle>
            <name>angularFormCaptcha</name>
            <userInputID>userCaptchaInput</userInputID>
            <codeLength>4-6</codeLength>
        </captchaStyle>
    </captchaStyles>
</botdetect>

Backend: ContactServlet.java

The src/main/java/com/captcha/botdetect/examples/angular/contact_form/
ContactServlet.java
source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
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();
        
        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 userEnteredCaptchaCode = formDataObj.get("userEnteredCaptchaCode").getAsString();
        String captchaId = formDataObj.get("captchaId").getAsString();

        // validate the form data
        Map<String, String> errors = new HashMap<String, String>();
        
        if (!isValidName(name)) {
            errors.put("name", "Name must be at least 3 chars long!");
        }
        
        if (!isValidEmail(email)) {
            errors.put("email", "Email is invalid!");
        }
        
        if (!isValidSubject(subject)) {
            errors.put("message", "Subject must be at least 10 chars long!");
        }
        
        if (!isValidMessage(message)) {
            errors.put("message", "Message must be at least 10 chars long!");
        }
        
        // validate the user entered captcha code        
        if (!isCaptchaCorrect(request, userEnteredCaptchaCode, captchaId)) {
            errors.put("userEnteredCaptchaCode", "CAPTCHA validation failed!");
            // TODO: consider logging the attempt
        }
        
        if (errors.isEmpty()) {
            // TODO: all validations succeeded; execute the protected action
            // (send email, write to database, etc...)
        }
        
        // create an object that stores the validation result
        ContactValidationResult validationResult = new ContactValidationResult();
        validationResult.setSuccess(errors.isEmpty());
        validationResult.setErrors(errors);
        
        try {
            // return the json string with the validation result to the frontend
            out.write(gson.toJson(validationResult));
        } finally {
            out.close();
        }
    }
    
    // the captcha validation function
    private boolean isCaptchaCorrect(HttpServletRequest request, String userEnteredCaptchaCode, String captchaId) {
        // create a captcha instance to be used for the captcha validation        
        SimpleCaptcha captcha = SimpleCaptcha.load(request);
        // execute the captcha validation        
        return captcha.validate(userEnteredCaptchaCode, 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);
    }
}

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;
    }
}

..............................................................

Angular-based Frontend and the plain PHP on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first Angular captcha integration by following our step-by-step Angular CAPTCHA Integration Guide.

Introduction

This contact form Angular captcha example shows the code required to display and validate captchas in a contact form of the application with a Angular frontend and the plain PHP backend.

Download the BotDetect PHP CAPTCHA Library archive and run this example

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

This example is in the <BDC-DIR>/examples/s_api-captcha-angular-plainphp/ folder; and contains the following files:

Frontend: app.module.ts

The angular-src/app/app.module.ts source file does the following:

  • Loads the BotDetect CAPTCHA Angular Module into the page
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
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 { BasicComponent }   from './basic/basic.component';
import { ContactComponent }   from './contact/contact.component';

import { BasicSuccessNotifyComponent }   from './notify/basic-notify/basic-success-notify.component';
import { ContactSuccessNotifyComponent }   from './notify/contact-notify/contact-success-notify.component';

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

@NgModule({
  declarations: [
    AppComponent,
    BasicComponent,
    ContactComponent,
    ValuesPipe,
    BasicSuccessNotifyComponent,
    ContactSuccessNotifyComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule,
    AppRoutingModule,
    // import Captcha Angular Module
    BotDetectCaptchaModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Frontend: contact.component.html

The angular-src/app/contact/contact.component.html source file contains the following:

  • Displays the captcha and user-input markups on the page
<form novalidate (ngSubmit)="send(contact)" [formGroup]="contact">

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>

  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input
      type="text"
      id="userCaptchaInput"
      name="userCaptchaInput"
      formControlName="userCaptchaInput"
    >
  </label>

  <button type="submit">Send</button>
</form>

Frontend: contact.component.ts

The angular-src/app/contact/contact.component.ts source file does the following:

  • Loads the captcha challenges from backend
  • Sets the captchaEndpoint property to point to the captcha endpoint path on backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
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.
   */
  
  // if you use Angular 2/4/5/6/7 use this line:
  @ViewChild(CaptchaComponent) captchaComponent: CaptchaComponent;

  // if you use Angular 8 use this line instead of the above one:
  // @ViewChild(CaptchaComponent, { static: true }) captchaComponent: CaptchaComponent;  

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

  ngOnInit(): void {
    this.contact = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['',  [Validators.required, Validators.pattern(this.emailRegex)]],
      subject: ['',  [Validators.required,Validators.minLength(10)]],
      message: ['',  [Validators.required,Validators.minLength(10)]],
      userCaptchaInput: [''] // we will validate user captcha input value at the backend side
    });

    // set the captchaEndpoint property to point to 
    // the captcha endpoint path on your app's backend
    this.captchaComponent.captchaEndpoint = 'botdetect-captcha-lib/simple-botdetect.php';
  }

  /**
   * Process the contact form on submit event.
   */
  send({ value, valid }: { value: Contact, valid: boolean }): void {

    if (valid) {

      // get the user-entered captcha code value to be validated at the backend side        
      let userEnteredCaptchaCode = this.captchaComponent.userEnteredCaptchaCode;
      
      // get the id of a captcha instance that the user tried to solve
      let captchaId = this.captchaComponent.captchaId;

      let postData = {
        name: value.name,
        email: value.email,
        subject: value.subject,
        message: value.message,
        // add the user-entered captcha code value to the post data    
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data      
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      this.contactService.send(postData)
        .subscribe(
          response => {
            if (response.success == false) {
              // captcha validation failed; show the error message
              this.errorMessages = response.errors;
              // call the this.captchaComponent.reloadImage()
              // in order to generate a new captcha challenge
              this.captchaComponent.reloadImage();
            } else {
              // captcha validation succeeded; proceed with the workflow
              this.router.navigate(['/contact-success-notify']) 
            }
          },
          error => {
            throw new Error(error);
          });

    } else {
      this.errorMessages = { formInvalid: 'Please enter valid values.' }
    }

  }
}

Frontend: contact.service.ts

The angular-src/app/contact/contact.service.ts source file does the following:

  • Sends the post request with the captcha id and the user entered captcha code to backend
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 = 'contact.php';

  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));
  }
}

Backend: botdetect.xml

The botdetect-captcha-lib/config/botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?xml version="1.0" encoding="UTF-8"?>
<botdetect xmlns="https://captcha.com/schema/php"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="https://captcha.com/schema/php
            https://captcha.com/schema/php/botdetect-4.2.5.xsd">

  <captchaStyles>
    <captchaStyle>
      <name>angularBasicCaptcha</name>
      <userInputID>userCaptchaInput</userInputID>
      <codeLength>3-5</codeLength>
    </captchaStyle>
  </captchaStyles>
</botdetect>

Backend: contact.php

The contact.php source file does the following:

  • Extracts the $userEnteredCaptchaCode and $captchaId values posted from the frontend
  • Validates captchas by calling the Validate($userEnteredCaptchaCode, $captchaId) method
  • Returns the json-formatted captcha validation results to frontend
<?php header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

  require('botdetect-captcha-lib/simple-botdetect.php');
  
  $postedData = (array) json_decode(file_get_contents('php://input'), true);

  $name = $postedData['name'];
  $email = $postedData['email'];
  $subject = $postedData['subject'];
  $message = $postedData['message'];
  $userEnteredCaptchaCode = $postedData['userEnteredCaptchaCode'];
  $captchaId = $postedData['captchaId'];

  // validate the form data
  $error = array();

  if (!isValidName($name)) {
    $error['name'] = "Name must be at least 3 chars long!";
  }

  if (!isValidEmail($email)) {
    $error['email'] = "Email is invalid!";
  }

  if (!isValidSubject($subject)) {
    $error['subject'] = "Subject must be at least 10 chars long!";
  }

  if (!isValidMessage($message)) {
    $error['message'] = "Message must be at least 10 chars long!";
  }

  // validate the user entered captcha code
  if (!isCaptchaCorrect($userEnteredCaptchaCode, $captchaId)) {
    $error['userEnteredCaptchaCode'] = "CAPTCHA validation failed!";
    // TODO: consider logging the attempt
  }

  if (empty($error)) {
    // TODO: all validations succeeded; execute the protected action
    // (send email, write to database, etc...)
  }

  // return the json string with the validation result to the frontend
  $result = array('success' => empty($error), 'errors' => $error);
  echo json_encode($result, true); exit;
}

// the captcha validation function
function isCaptchaCorrect($userEnteredCaptchaCode, $captchaId) {
  // create a captcha instance to be used for the captcha validation
  $captcha = new SimpleCaptcha();
  // execute the captcha validation
  return $captcha->Validate($userEnteredCaptchaCode, $captchaId);
}

function isValidName($name) {
  if($name == null) {
    return false;
  }
  return (strlen($name) >= 3);
}

function isValidEmail($email) {
  if($email == null) {
    return false;
  }

  return preg_match("/^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$/", $email, $matches);
}

function isValidSubject($subject) {
  if($subject == null) {
    return false;
  }

  return (strlen($subject) > 9) && (strlen($subject) < 255);
}

function isValidMessage($message) {
  if($message == null) {
    return false;
  }

  return (strlen($message) > 9) && (strlen($message) < 255);
}

..............................................................

Please Note

The new experimental Simple API is still work-in-progress. This Integration Guide & Examples require you to have the Angular Captcha Module v3.7.4 or AngularJS Captcha Module v1.6.1 on frontend, and one of the following BotDetect CAPTCHA releases on backend: ASP.NET v4.4.2+, Java v4.0.Beta3.7+, PHP v4.2.5+.

Click on the link below that matches the Angular version that you are using:

..............................................................

AngularJS-based Frontend

Click on the link below that matches your BotDetect CAPTCHA AngularJS Module usage scenario:

First Time Here?

The AngularJS CAPTCHA Integration Guide is the best place to start!

..............................................................

AngularJS-based Frontend and the web API with MVC Core 1/2 on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first AngularJS captcha integration by following our step-by-step AngularJS CAPTCHA Integration Guide.

Introduction

This contact form AngularJS captcha example shows the code required to display and validate captchas in a contact form of the application with a AngularJS frontend and a web API with MVC Core 1/2 backend.

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

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

This example is in the <BDC-DIR>/core-on-core/examples/s_api-captcha-angularjs-webapi_mvccore2/ folder; and contains the following files:

Frontend: index.html

The wwwroot/index.html source file does the following:

  • Loads the BotDetect CAPTCHA AngularJS Module into the page
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BotDetect AngularJS CAPTCHA Examples</title>
  <link href="styles/styles.css" type="text/css" rel="stylesheet">
</head>
<body>
  <header>
    <div class="header-content"><h1>BotDetect AngularJS CAPTCHA Examples</h1></div>
  </header>

  <nav ng-controller="NavigationController">
    <ul class="nav">
      <li><a href="#/basic" ng-class="{ active: isActive('/basic')}">Basic Example</a></li>
        <li><a href="#/contact" ng-class="{ active: isActive('/contact')}">Contact Example</a></li>
    </ul>
  </nav>

  <section id="main-content">
    <ng-view></ng-view>
  </section>

  
  <script src="node_modules/angularjs/angular.min.js"></script>
  <script src="node_modules/angularjs/angular-route.min.js"></script>

  <script src="node_modules/angularjs-captcha/dist/angularjs-captcha.min.js"></script>
  <script src="app.js"></script>
</body>
</html>

Frontend: contact-captcha.html

The wwwroot/templates/contact/contact-captcha.html source file contains the following:

  • The captcha and user-input markup that are going to be displayed on the page
<form name="contactForm" novalidate ng-controller="ContactController" ng-submit="send(contactForm.$valid)" >

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>
  
  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input type="text" 
        name="userCaptchaInput" 
        id="userCaptchaInput" 
        ng-model="userCaptchaInput"
    >
  </label>

  <button type="submit" class="btn btn-primary">Send</button>
</form>

Frontend: app.js

The wwwroot/app.js source file does the following:

  • Loads the captcha challenges from backend
  • Sends the post request with the captcha id and the user entered captcha code to backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
var app = angular.module('app', ['BotDetectCaptcha', 'ngRoute']);

app.config(function ($routeProvider, captchaSettingsProvider) {
  $routeProvider
    .when('/basic', { templateUrl: 'templates/basic/basic-captcha.html' })
    .when('/contact', { templateUrl: 'templates/contact/contact-captcha.html' })
    .when('/basic-success-notify', { templateUrl: 'templates/notify/basic-success-notify.html' })
    .when('/contact-success-notify', { templateUrl: 'templates/notify/contact-success-notify.html' })
    .otherwise({ redirectTo: '/basic' });

  // set the captchaEndpoint property to point to 
  // the captcha endpoint path on your app's backend
  captchaSettingsProvider.setSettings({
    captchaEndpoint: 'simple-captcha-endpoint.ashx'
  });
});

app.controller('ContactController', function ($scope, $http, $window, Captcha) {

  // validation messages
  $scope.errorMessages = '';

  // process the contact form on submit event
  $scope.send = function (contactFormValid) {

    // create new AngularJS Captcha instance
    var captcha = new Captcha();

    if (contactFormValid) {

      // get the user-entered captcha code value to be validated at the backend side        
      var userEnteredCaptchaCode = captcha.getUserEnteredCaptchaCode();

      // get the id of a captcha instance that the user tried to solve
      var captchaId = captcha.getCaptchaId();

      var postData = {
        name: $scope.name,
        email: $scope.email,
        subject: $scope.subject,
        message: $scope.message,
        // add the user-entered captcha code value to the post data            
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data            
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      $http({
        method: 'POST',
        url: 'api/contact-captcha',
        data: postData
      })
        .then(function (response) {
          if (response.data.success == false) {
            // captcha validation failed; show the error message
            $scope.errorMessages = response.data.errors;
            // call the captcha.reloadImage()
            // in order to generate a new captcha challenge
            captcha.reloadImage();
          } else {
            // captcha validation succeeded; proceed with the workflow
            $window.location.href = '#/contact-success-notify';
          }
        }, function (error) {
          console.log(error.data);
        });
    } else {
      $scope.errorMessages = { formInvalid: 'Please enter valid values.' };
    }
  };

});

Backend: appsettings.json

The appsettings.json source file contains the following:

  • BotDetect configuration section that defines the CaptchaEndpointPath path
{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "BotDetect": {
    "CaptchaEndpointPath": "simple-captcha-endpoint.ashx"
  }
}

Backend: Startup.cs

The Startup.cs source file does the following:

  • Configures the application pipeline to use SimpleCaptcha middleware
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using BotDetect.Web;
using BotDetect.Web.Http;

namespace AngularJSWebAPIwithMVC6CaptchaExample
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // You can omit the next line on .NET Core 2.0 or higher
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddMemoryCache(); // Adds a default in-memory 
                                       // implementation of 
                                       // IDistributedCache

            // Add framework services.
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseDefaultFiles(options);
            app.UseStaticFiles();

            // Configure the application pipeline to use SimpleCaptcha middleware
            app.UseSimpleCaptcha(Configuration.GetSection("BotDetect"));

            app.UseMvc();
        }
    }
}

Backend: botdetect.xml

The botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.2.xsd">

  <captchaStyles>
    <captchaStyle>
      <name>angularFormCaptcha</name>
      <userInputID>userCaptchaInput</userInputID>
      <codeLength>4-6</codeLength>
    </captchaStyle>
  </captchaStyles>
</botdetect>

Backend: ContactApiController.cs

The Controllers/ContactApiController.cs source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the Validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using BotDetect.Web;

namespace AngularJSWebAPIwithMVC6CaptchaExample.Controllers
{
	[Route("api/contact-captcha")]
    public class ContactApiController : Controller
    {
        [HttpPost]
        public IActionResult Post([FromBody] Models.ContactFormModel data)
        {
            string name = data.Name;
            string email = data.Email;
            string subject = data.Subject;
            string message = data.Message;

            string userEnteredCaptchaCode = data.UserEnteredCaptchaCode;
            string captchaId = data.CaptchaId;

            // validate the form data
            Dictionary<string, string> errors = new Dictionary<string, string>();

            if (!IsValidName(name))
            {
                errors.Add("name", "Name must be at least 3 chars long!");
            }

            if (!IsValidEmail(email))
            {
                errors.Add("email", "Email is invalid!");
            }

            if (!IsValidSubject(subject))
            {
                errors.Add("subject", "Subject must be at least 10 chars long!");
            }

            if (!IsValidMessage(message))
            {
                errors.Add("message", "Message must be at least 10 chars long!");
            }

            // validate the user entered captcha code
            if (!IsCaptchaCorrect(userEnteredCaptchaCode, captchaId))
            {
                errors.Add("userEnteredCaptchaCode", "CAPTCHA validation failed!");
                // TODO: consider logging the attempt
            }

            bool isErrorsEmpty = errors.Count > 0 ? false : true;

            if (isErrorsEmpty)
            {
                // TODO: all validations succeeded; execute the protected action
                // (send email, write to database, etc...)
            }

            // create an object that stores the validation result
            Dictionary<string, object> validationResult = new Dictionary<string, object>();
            validationResult.Add("success", isErrorsEmpty);
            validationResult.Add("errors", errors);

            // return the json string with the validation result to the frontend
            return Json(validationResult);
        }


        // the captcha validation function
        private bool IsCaptchaCorrect(string userEnteredCaptchaCode, string captchaId)
        {
            // create a captcha instance to be used for the captcha validation
            SimpleCaptcha captcha = new SimpleCaptcha();
            // execute the captcha validation
            return captcha.Validate(userEnteredCaptchaCode, captchaId);
        }

        private bool IsValidName(string name)
        {
            if (name == null)
            {
                return false;
            }

            return (name.Length >= 3);
        }

        private bool IsValidEmail(string email)
        {
            if (email == null)
            {
                return false;
            }
            Regex regex = new Regex(@"^[\w-_\.+]*[\w-_\.]\@([\w]+\.)+[\w]+[\w]$");
            Match match = regex.Match(email);

            return match.Success;
        }

        private bool IsValidSubject(string subject)
        {
            if (subject == null)
            {
                return false;
            }

            return (subject.Length > 9) && (subject.Length < 255);
        }

        private bool IsValidMessage(string message)
        {
            if (message == null)
            {
                return false;
            }

            return (message.Length > 9) && (message.Length < 255);
        }
    }
}

..............................................................

AngularJS-based Frontend and the Legacy ASP.NET Web-API 2 on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first AngularJS captcha integration by following our step-by-step .

Introduction

This contact form AngularJS captcha example shows the code required to display and validate captchas in a contact form of the application with a AngularJS frontend and a legacy ASP.NET Web-API 2 backend.

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

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/s_api-captcha-angularjs-webapi2/csharp/ folder; and contains the following files:

Frontend: index.html

The index.html source file does the following:

  • Loads the BotDetect CAPTCHA AngularJS Module into the page
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BotDetect AngularJS CAPTCHA Examples</title>
  <link href="styles/styles.css" type="text/css" rel="stylesheet">
</head>
<body>
  <header>
    <div class="header-content"><h1>BotDetect AngularJS CAPTCHA Examples</h1></div>
  </header>

  <nav ng-controller="NavigationController">
    <ul class="nav">
      <li><a href="#/basic" ng-class="{ active: isActive('/basic')}">Basic Example</a></li>
        <li><a href="#/contact" ng-class="{ active: isActive('/contact')}">Contact Example</a></li>
    </ul>
  </nav>

  <section id="main-content">
    <ng-view></ng-view>
  </section>

  
  <script src="node_modules/angularjs/angular.min.js"></script>
  <script src="node_modules/angularjs/angular-route.min.js"></script>

  <script src="node_modules/angularjs-captcha/dist/angularjs-captcha.min.js"></script>
  <script src="app.js"></script>
</body>
</html>

Frontend: contact-captcha.html

The templates/contact/contact-captcha.html source file contains the following:

  • The captcha and user-input markup that are going to be displayed on the page
<form name="contactForm" novalidate ng-controller="ContactController" ng-submit="send(contactForm.$valid)" >

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>
  
  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input type="text" 
        name="userCaptchaInput" 
        id="userCaptchaInput" 
        ng-model="userCaptchaInput"
    >
  </label>

  <button type="submit" class="btn btn-primary">Send</button>
</form>

Frontend: app.js

The app.js source file does the following:

  • Loads the captcha challenges from backend
  • Sends the post request with the captcha id and the user entered captcha code to backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
var app = angular.module('app', ['BotDetectCaptcha', 'ngRoute']);

app.config(function ($routeProvider, captchaSettingsProvider) {
  $routeProvider
    .when('/basic', { templateUrl: 'templates/basic/basic-captcha.html' })
    .when('/contact', { templateUrl: 'templates/contact/contact-captcha.html' })
    .when('/basic-success-notify', { templateUrl: 'templates/notify/basic-success-notify.html' })
    .when('/contact-success-notify', { templateUrl: 'templates/notify/contact-success-notify.html' })
    .otherwise({ redirectTo: '/basic' });

  // set the captchaEndpoint property to point to 
  // the captcha endpoint path on your app's backend
  captchaSettingsProvider.setSettings({
    captchaEndpoint: 'simple-captcha-endpoint.ashx'
  });
});

app.controller('ContactController', function ($scope, $http, $window, Captcha) {

  // validation messages
  $scope.errorMessages = '';

  // process the contact form on submit event
  $scope.send = function (contactFormValid) {

    // create new AngularJS Captcha instance
    var captcha = new Captcha();

    if (contactFormValid) {

      // get the user-entered captcha code value to be validated at the backend side        
      var userEnteredCaptchaCode = captcha.getUserEnteredCaptchaCode();

      // get the id of a captcha instance that the user tried to solve
      var captchaId = captcha.getCaptchaId();

      var postData = {
        name: $scope.name,
        email: $scope.email,
        subject: $scope.subject,
        message: $scope.message,
        // add the user-entered captcha code value to the post data            
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data            
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      $http({
        method: 'POST',
        url: 'api/webapi/contact',
        data: postData
      })
        .then(function (response) {
          if (response.data.success == false) {
            // captcha validation failed; show the error message
            $scope.errorMessages = response.data.errors;
            // call the captcha.reloadImage()
            // in order to generate a new captcha challenge
            captcha.reloadImage();
          } else {
            // captcha validation succeeded; proceed with the workflow
            $window.location.href = '#/contact-success-notify';
          }
        }, function (error) {
          console.log(error.data);
        });
    } else {
      $scope.errorMessages = { formInvalid: 'Please enter valid values.' };
    }
  };

});

Backend: web.config

The web.config source file does the following:

  • Registers the simple-captcha-endpoint.ashx path
  • Maps it to the BotDetect.Web.SimpleCaptchaHandler
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings></appSettings>
  <system.web>
    <compilation debug="false" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="simple-captcha-endpoint.ashx"
	   type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </httpHandlers>
  </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="simple-captcha-endpoint.ashx"
	   type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
</configuration>

Backend: botdetect.xml

The botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.2.xsd">

    <captchaStyles>
        <captchaStyle>
            <name>angularFormCaptcha</name>
            <userInputID>userCaptchaInput</userInputID>
            <codeLength>4-6</codeLength>
        </captchaStyle>
    </captchaStyles>
</botdetect>

Backend: WebApiController.cs

The Backend/Controllers/WebApiController.cs source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the Validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Http;

using BotDetect.Web;

namespace AngularWebAPICaptchaExampleCSharp.Backend.Controllers
{
    public class WebApiController : ApiController
    {

        // POST api/webapi/contact
        public IHttpActionResult Contact([FromBody]CaptchaContactModel data)
        {
            string name = data.Name;
            string email = data.Email;
            string subject = data.Subject;
            string message = data.Message;

            string userEnteredCaptchaCode = data.UserEnteredCaptchaCode;
            string captchaId = data.CaptchaId;

            // validate the form data
            Dictionary<string, string> errors = new Dictionary<string, string>();

            if (!IsValidName(name))
            {
                errors.Add("name", "Name must be at least 3 chars long!");
            }

            if (!IsValidEmail(email))
            {
                errors.Add("email", "Email is invalid!");
            }

            if (!IsValidSubject(subject))
            {
                errors.Add("subject", "Subject must be at least 10 chars long!");
            }

            if (!IsValidMessage(message))
            {
                errors.Add("message", "Message must be at least 10 chars long!");
            }

            // validate the user entered captcha code
            if (!IsCaptchaCorrect(userEnteredCaptchaCode, captchaId))
            {
                errors.Add("userEnteredCaptchaCode", "CAPTCHA validation failed!");
                // TODO: consider logging the attempt
            }

            bool isErrorsEmpty = errors.Count <= 0;

            if (isErrorsEmpty)
            {
                // TODO: all validations succeeded; execute the protected action
                // (send email, write to database, etc...)
            }

            // create an object that stores the validation result
            Dictionary<string, object> validationResult = new Dictionary<string, object>();
            validationResult.Add("success", isErrorsEmpty);
            validationResult.Add("errors", errors);

            // return the json string with the validation result to the frontend
            return Ok(validationResult);
        }

        // the captcha validation function
        private bool IsCaptchaCorrect(string userEnteredCaptchaCode, string captchaId)
        {
            // create a captcha instance to be used for the captcha validation
            SimpleCaptcha captcha = new SimpleCaptcha();
            // execute the captcha validation
            return captcha.Validate(userEnteredCaptchaCode, captchaId);
        }

        private bool IsValidName(string name)
        {
            if (name == null)
            {
                return false;
            }

            return (name.Length >= 3);
        }

        private bool IsValidEmail(string email)
        {
            if (email == null)
            {
                return false;
            }
            Regex regex = new Regex("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$");
            Match match = regex.Match(email);

            return match.Success;
        }

        private bool IsValidSubject(string subject)
        {
            if (subject == null)
            {
                return false;
            }

            return (subject.Length > 9) && (subject.Length < 255);
        }

        private bool IsValidMessage(string message)
        {
            if (message == null)
            {
                return false;
            }

            return (message.Length > 9) && (message.Length < 255);
        }

    }

    public class CaptchaContactModel
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("email")]
        public string Email { get; set; }

        [JsonProperty("subject")]
        public string Subject { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }

        [JsonProperty("captchaId")]
        public string CaptchaId { get; set; }

        [JsonProperty("userEnteredCaptchaCode")]
        public string UserEnteredCaptchaCode { get; set; }
    }

}

..............................................................

AngularJS-based Frontend and the Legacy ASP.NET Generic Handler on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first AngularJS captcha integration by following our step-by-step AngularJS CAPTCHA Integration Guide.

Introduction

This contact form AngularJS captcha example shows the code required to display and validate captchas in a contact form of the application with a AngularJS frontend and a legacy ASP.NET Generic Handler backend.

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

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/s_api-captcha-angularjs-generic_handler/csharp/ folder; and contains the following files:

Frontend: index.html

The index.html source file does the following:

  • Loads the BotDetect CAPTCHA AngularJS Module into the page
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BotDetect AngularJS CAPTCHA Examples</title>
  <link href="styles/styles.css" type="text/css" rel="stylesheet">
</head>
<body>
  <header>
    <div class="header-content"><h1>BotDetect AngularJS CAPTCHA Examples</h1></div>
  </header>

  <nav ng-controller="NavigationController">
    <ul class="nav">
      <li><a href="#/basic" ng-class="{ active: isActive('/basic')}">Basic Example</a></li>
        <li><a href="#/contact" ng-class="{ active: isActive('/contact')}">Contact Example</a></li>
    </ul>
  </nav>

  <section id="main-content">
    <ng-view></ng-view>
  </section>

  
  <script src="node_modules/angularjs/angular.min.js"></script>
  <script src="node_modules/angularjs/angular-route.min.js"></script>

  <script src="node_modules/angularjs-captcha/dist/angularjs-captcha.min.js"></script>
  <script src="app.js"></script>
</body>
</html>

Frontend: contact-captcha.html

The templates/contact/contact-captcha.html source file contains the following:

  • The captcha and user-input markup that are going to be displayed on the page
<form name="contactForm" novalidate ng-controller="ContactController" ng-submit="send(contactForm.$valid)" >

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>
  
  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input type="text" 
        name="userCaptchaInput" 
        id="userCaptchaInput" 
        ng-model="userCaptchaInput"
    >
  </label>

  <button type="submit" class="btn btn-primary">Send</button>
</form>

Frontend: app.js

The app.js source file does the following:

  • Loads the captcha challenges from backend
  • Sends the post request with the captcha id and the user entered captcha code to backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
var app = angular.module('app', ['BotDetectCaptcha', 'ngRoute']);

app.config(function ($routeProvider, captchaSettingsProvider) {
  $routeProvider
    .when('/basic', { templateUrl: 'templates/basic/basic-captcha.html' })
    .when('/contact', { templateUrl: 'templates/contact/contact-captcha.html' })
    .when('/basic-success-notify', { templateUrl: 'templates/notify/basic-success-notify.html' })
    .when('/contact-success-notify', { templateUrl: 'templates/notify/contact-success-notify.html' })
    .otherwise({ redirectTo: '/basic' });

  // set the captchaEndpoint property to point to 
  // the captcha endpoint path on your app's backend
  captchaSettingsProvider.setSettings({
    captchaEndpoint: 'simple-captcha-endpoint.ashx'
  });
});

app.controller('ContactController', function ($scope, $http, $window, Captcha) {

  // validation messages
  $scope.errorMessages = '';

  // process the contact form on submit event
  $scope.send = function (contactFormValid) {

    // create new AngularJS Captcha instance
    var captcha = new Captcha();

    if (contactFormValid) {

      // get the user-entered captcha code value to be validated at the backend side        
      var userEnteredCaptchaCode = captcha.getUserEnteredCaptchaCode();

      // get the id of a captcha instance that the user tried to solve
      var captchaId = captcha.getCaptchaId();

      var postData = {
        name: $scope.name,
        email: $scope.email,
        subject: $scope.subject,
        message: $scope.message,
        // add the user-entered captcha code value to the post data            
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data            
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      $http({
        method: 'POST',
        url: 'ContactHandler.ashx',
        data: postData
      })
        .then(function (response) {
          if (response.data.success == false) {
            // captcha validation failed; show the error message
            $scope.errorMessages = response.data.errors;
            // call the captcha.reloadImage()
            // in order to generate a new captcha challenge
            captcha.reloadImage();
          } else {
            // captcha validation succeeded; proceed with the workflow
            $window.location.href = '#/contact-success-notify';
          }
        }, function (error) {
          console.log(error.data);
        });
    } else {
      $scope.errorMessages = { formInvalid: 'Please enter valid values.' };
    }
  };

});

Backend: web.config

The web.config source file does the following:

  • Registers the simple-captcha-endpoint.ashx path
  • Maps it to the BotDetect.Web.SimpleCaptchaHandler
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5"/>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="simple-captcha-endpoint.ashx"
           type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </httpHandlers>
  </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="simple-captcha-endpoint.ashx"
           type="BotDetect.Web.SimpleCaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
</configuration>

Backend: botdetect.xml

The botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.2.xsd">

    <captchaStyles>
        <captchaStyle>
            <name>angularFormCaptcha</name>
            <userInputID>userCaptchaInput</userInputID>
            <codeLength>4-6</codeLength>
        </captchaStyle>
    </captchaStyles>
</botdetect>

Backend: ContactHandler.ashx

The ContactHandler.ashx source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the Validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
<%@ WebHandler Language="C#" Class="ContactHandler" %>

using System;
using System.Web;
using System.IO;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using BotDetect.Web;

public class ContactHandler : 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 name = formDataObj["name"];
            string email = formDataObj["email"];
            string subject = formDataObj["subject"];
            string message = formDataObj["message"];

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

            // validate the form data
            Dictionary<string, string> errors = new Dictionary<string, string>();

            if (!IsValidName(name))
            {
                errors.Add("name", "Name must be at least 3 chars long!");
            }

            if (!IsValidEmail(email))
            {
                errors.Add("email", "Email is invalid!");
            }

            if (!IsValidSubject(subject))
            {
                errors.Add("subject", "Subject must be at least 10 chars long!");
            }

            if (!IsValidMessage(message))
            {
                errors.Add("message", "Message must be at least 10 chars long!");
            }

            // validate the user entered captcha code
            if (!IsCaptchaCorrect(userEnteredCaptchaCode, captchaId))
            {
                errors.Add("userEnteredCaptchaCode", "CAPTCHA validation failed!");
                // TODO: consider logging the attempt
            }

            bool isErrorsEmpty = (errors.Count > 0) ? false : true;

            if (isErrorsEmpty)
            {
                // TODO: all validations succeeded; execute the protected action
                // (send email, write to database, etc...)
            }

            // create an object that stores the validation result
            Dictionary<string, object> validationResult = new Dictionary<string, object>();
            validationResult.Add("success", isErrorsEmpty);
            validationResult.Add("errors", errors);

            // return the json string with the validation result to the frontend
            context.Response.ContentType = "application/json; charset=utf-8";
            context.Response.Write(JsonConvert.SerializeObject(validationResult));
        }
        else
        {
            context.Response.ContentType = "text/plain";
            context.Response.Write("Only HTTP POST requests are allowed.");
        }
    }

    // the captcha validation function
    private bool IsCaptchaCorrect(string userEnteredCaptchaCode, string captchaId)
    {
        // create a captcha instance to be used for the captcha validation
        SimpleCaptcha captcha = new SimpleCaptcha();
        // execute the captcha validation
        return captcha.Validate(userEnteredCaptchaCode, captchaId);
    }

    private bool IsValidName(string name)
    {
        if (name == null)
        {
            return false;
        }

        return (name.Length >= 3);
    }

    private bool IsValidEmail(string email)
    {
        if (email == null)
        {
            return false;
        }
        Regex regex = new Regex("^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$");
        Match match = regex.Match(email);

        return match.Success;
    }

    private bool IsValidSubject(string subject)
    {
        if (subject == null)
        {
            return false;
        }

        return (subject.Length > 9) && (subject.Length < 255);
    }

    private bool IsValidMessage(string message)
    {
        if (message == null)
        {
            return false;
        }

        return (message.Length > 9) && (message.Length < 255);
    }

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

..............................................................

AngularJS-based Frontend and the Java Servlet on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first AngularJS captcha integration by following our step-by-step AngularJS CAPTCHA Integration Guide.

Introduction

This contact form AngularJS captcha example shows the code required to display and validate captchas in a contact form of the application with a AngularJS frontend and a Java Servlet backend.

Download the BotDetect Java CAPTCHA Library archive and run this example

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

This example is in the <BDC-DIR>/examples/s_api-captcha-angularjs-servlet/ folder; and contains the following files:

Frontend: index.html

The src/main/webapp/index.html source file does the following:

  • Loads the BotDetect CAPTCHA AngularJS Module into the page
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BotDetect AngularJS CAPTCHA Examples</title>
  <link href="styles/styles.css" type="text/css" rel="stylesheet">
</head>
<body>
  <header>
    <div class="header-content"><h1>BotDetect AngularJS CAPTCHA Examples</h1></div>
  </header>

  <nav ng-controller="NavigationController">
    <ul class="nav">
      <li><a href="#/basic" ng-class="{ active: isActive('/basic')}">Basic Example</a></li>
        <li><a href="#/contact" ng-class="{ active: isActive('/contact')}">Contact Example</a></li>
    </ul>
  </nav>

  <section id="main-content">
    <ng-view></ng-view>
  </section>

  
  <script src="node_modules/angularjs/angular.min.js"></script>
  <script src="node_modules/angularjs/angular-route.min.js"></script>

  <script src="node_modules/angularjs-captcha/dist/angularjs-captcha.min.js"></script>
  <script src="app.js"></script>
</body>
</html>

Frontend: contact-captcha.html

The src/main/webapp/templates/contact/contact-captcha.html source file contains the following:

  • The captcha and user-input markup that are going to be displayed on the page
<form name="contactForm" novalidate ng-controller="ContactController" ng-submit="send(contactForm.$valid)" >

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>
  
  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input type="text" 
        name="userCaptchaInput" 
        id="userCaptchaInput" 
        ng-model="userCaptchaInput"
    >
  </label>

  <button type="submit" class="btn btn-primary">Send</button>
</form>

Frontend: app.js

The src/main/webapp/app.js source file does the following:

  • Loads the captcha challenges from backend
  • Sends the post request with the captcha id and the user entered captcha code to backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
var app = angular.module('app', ['BotDetectCaptcha', 'ngRoute']);

app.config(function ($routeProvider, captchaSettingsProvider) {
  $routeProvider
    .when('/basic', { templateUrl: 'templates/basic/basic-captcha.html' })
    .when('/contact', { templateUrl: 'templates/contact/contact-captcha.html' })
    .when('/basic-success-notify', { templateUrl: 'templates/notify/basic-success-notify.html' })
    .when('/contact-success-notify', { templateUrl: 'templates/notify/contact-success-notify.html' })
    .otherwise({ redirectTo: '/basic' });

  // set the captchaEndpoint property to point to 
  // the captcha endpoint path on your app's backend
  captchaSettingsProvider.setSettings({
    captchaEndpoint: 'simple-captcha-endpoint'
  });
});

app.controller('ContactController', function ($scope, $http, $window, Captcha) {

  // validation messages
  $scope.errorMessages = '';

  // process the contact form on submit event
  $scope.send = function (contactFormValid) {

    // create new AngularJS Captcha instance
    var captcha = new Captcha();

    if (contactFormValid) {

      // get the user-entered captcha code value to be validated at the backend side        
      var userEnteredCaptchaCode = captcha.getUserEnteredCaptchaCode();

      // get the id of a captcha instance that the user tried to solve
      var captchaId = captcha.getCaptchaId();

      var postData = {
        name: $scope.name,
        email: $scope.email,
        subject: $scope.subject,
        message: $scope.message,
        // add the user-entered captcha code value to the post data            
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data            
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      $http({
        method: 'POST',
        url: 'contact',
        data: postData
      })
        .then(function (response) {
          if (response.data.success == false) {
            // captcha validation failed; show the error message
            $scope.errorMessages = response.data.errors;
            // call the captcha.reloadImage()
            // in order to generate a new captcha challenge
            captcha.reloadImage();
          } else {
            // captcha validation succeeded; proceed with the workflow
            $window.location.href = '#/contact-success-notify';
          }
        }, function (error) {
          console.log(error.data);
        });
    } else {
      $scope.errorMessages = { formInvalid: 'Please enter valid values.' };
    }
  };

});

Backend: web.xml

The src/main/webapp/WEB-INF/web.xml source file does the following:

  • Registers the simple-captcha-endpoint path
  • Maps it to the com.captcha.botdetect.web.servlet.SimpleCaptchaServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee                 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <servlet>
        <servlet-name>BotDetect Captcha</servlet-name>
        <servlet-class>com.captcha.botdetect.web.servlet.SimpleCaptchaServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BotDetect Captcha</servlet-name>
        <url-pattern>/simple-captcha-endpoint</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>Contact Captcha</servlet-name>
        <servlet-class>com.captcha.botdetect.examples.angularjs.contact_form.ContactServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Contact Captcha</servlet-name>
        <url-pattern>/contact</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

Backend: botdetect.xml

The src/main/webapp/WEB-INF/botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?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.7.xsd">

    <captchaStyles>
        <captchaStyle>
            <name>angularFormCaptcha</name>
            <userInputID>userCaptchaInput</userInputID>
            <codeLength>4-6</codeLength>
        </captchaStyle>
    </captchaStyles>
</botdetect>

Backend: ContactServlet.java

The src/main/java/com/captcha/botdetect/examples/angularjs/contact_form/
ContactServlet.java
source file does the following:

  • Extracts the userEnteredCaptchaCode and captchaId values posted from the frontend
  • Validates captchas by calling the validate(userEnteredCaptchaCode, captchaId) method
  • Returns the json-formatted captcha validation results to frontend
package com.captcha.botdetect.examples.angularjs.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();
        
        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 userEnteredCaptchaCode = formDataObj.get("userEnteredCaptchaCode").getAsString();
        String captchaId = formDataObj.get("captchaId").getAsString();

        // validate the form data
        Map<String, String> errors = new HashMap<String, String>();
        
        if (!isValidName(name)) {
            errors.put("name", "Name must be at least 3 chars long!");
        }
        
        if (!isValidEmail(email)) {
            errors.put("email", "Email is invalid!");
        }
        
        if (!isValidSubject(subject)) {
            errors.put("message", "Subject must be at least 10 chars long!");
        }
        
        if (!isValidMessage(message)) {
            errors.put("message", "Message must be at least 10 chars long!");
        }
        
        // validate the user entered captcha code        
        if (!isCaptchaCorrect(request, userEnteredCaptchaCode, captchaId)) {
            errors.put("userEnteredCaptchaCode", "CAPTCHA validation failed!");
            // TODO: consider logging the attempt
        }
        
        if (errors.isEmpty()) {
            // TODO: all validations succeeded; execute the protected action
            // (send email, write to database, etc...)
        }
        
        // create an object that stores the validation result
        ContactValidationResult validationResult = new ContactValidationResult();
        validationResult.setSuccess(errors.isEmpty());
        validationResult.setErrors(errors);
        
        try {
            // return the json string with the validation result to the frontend
            out.write(gson.toJson(validationResult));
        } finally {
            out.close();
        }
    }
    
    // the captcha validation function
    private boolean isCaptchaCorrect(HttpServletRequest request, String userEnteredCaptchaCode, String captchaId) {
        // create a captcha instance to be used for the captcha validation        
        SimpleCaptcha captcha = SimpleCaptcha.load(request);
        // execute the captcha validation        
        return captcha.validate(userEnteredCaptchaCode, 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);
    }
}

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;
    }
}

..............................................................

AngularJS-based Frontend and the plain PHP on Backend

This is a partial walk-through of the example source code. It is not meant to replace a proper step-by-step guide. If you are new to BotDetect, skip it for now; and do start your first AngularJS captcha integration by following our step-by-step AngularJS CAPTCHA Integration Guide.

Introduction

This contact form AngularJS captcha example shows the code required to display and validate captchas in a contact form of the application with a AngularJS frontend and the plain PHP backend.

Download the BotDetect PHP CAPTCHA Library archive and run this example

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

This example is in the <BDC-DIR>/examples/s_api-captcha-angularjs-plainphp/ folder; and contains the following files:

Frontend: index.html

The index.html source file does the following:

  • Loads the BotDetect CAPTCHA AngularJS Module into the page
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>BotDetect AngularJS CAPTCHA Examples</title>
  <link href="styles/styles.css" type="text/css" rel="stylesheet">
</head>
<body>
  <header>
    <div class="header-content"><h1>BotDetect AngularJS CAPTCHA Examples</h1></div>
  </header>

  <nav ng-controller="NavigationController">
    <ul class="nav">
      <li><a href="#/basic" ng-class="{ active: isActive('/basic')}">Basic Example</a></li>
        <li><a href="#/contact" ng-class="{ active: isActive('/contact')}">Contact Example</a></li>
    </ul>
  </nav>

  <section id="main-content">
    <ng-view></ng-view>
  </section>

  
  <script src="node_modules/angularjs/angular.min.js"></script>
  <script src="node_modules/angularjs/angular-route.min.js"></script>

  <script src="node_modules/angularjs-captcha/dist/angularjs-captcha.min.js"></script>
  <script src="app.js"></script>
</body>
</html>

Frontend: contact-captcha.html

The templates/contact/contact-captcha.html source file contains the following:

  • The captcha and user-input markup that are going to be displayed on the page
<form name="contactForm" novalidate ng-controller="ContactController" ng-submit="send(contactForm.$valid)" >

  <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 chars long!
  </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 chars long!
  </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 chars long!
  </div>
  
  
  <!-- captcha challenge: placeholder -->
  <botdetect-captcha captchaStyleName="angularFormCaptcha"></botdetect-captcha>
  
  <label>
    <span>Retype the chars long from the picture:</span>
    <!-- captcha code: user-input textbox -->
    <input type="text" 
        name="userCaptchaInput" 
        id="userCaptchaInput" 
        ng-model="userCaptchaInput"
    >
  </label>

  <button type="submit" class="btn btn-primary">Send</button>
</form>

Frontend: app.js

The app.js source file does the following:

  • Loads the captcha challenges from backend
  • Sends the post request with the captcha id and the user entered captcha code to backend
  • Processes the captcha validation results:
    • when the captcha validation fails -- it reloads captcha and displays an error message
    • when the captcha validation succeeds -- it executes by the captcha protected action
var app = angular.module('app', ['BotDetectCaptcha', 'ngRoute']);

app.config(function ($routeProvider, captchaSettingsProvider) {
  $routeProvider
    .when('/basic', { templateUrl: 'templates/basic/basic-captcha.html' })
    .when('/contact', { templateUrl: 'templates/contact/contact-captcha.html' })
    .when('/basic-success-notify', { templateUrl: 'templates/notify/basic-success-notify.html' })
    .when('/contact-success-notify', { templateUrl: 'templates/notify/contact-success-notify.html' })
    .otherwise({ redirectTo: '/basic' });

  // set the captchaEndpoint property to point to 
  // the captcha endpoint path on your app's backend
  captchaSettingsProvider.setSettings({
    captchaEndpoint: 'botdetect-captcha-lib/simple-botdetect.php'
  });
});

app.controller('ContactController', function ($scope, $http, $window, Captcha) {

  // validation messages
  $scope.errorMessages = '';

  // process the contact form on submit event
  $scope.send = function (contactFormValid) {

    // create new AngularJS Captcha instance
    var captcha = new Captcha();

    if (contactFormValid) {

      // get the user-entered captcha code value to be validated at the backend side        
      var userEnteredCaptchaCode = captcha.getUserEnteredCaptchaCode();

      // get the id of a captcha instance that the user tried to solve
      var captchaId = captcha.getCaptchaId();

      var postData = {
        name: $scope.name,
        email: $scope.email,
        subject: $scope.subject,
        message: $scope.message,
        // add the user-entered captcha code value to the post data            
        userEnteredCaptchaCode: userEnteredCaptchaCode,
        // add the id of a captcha instance to the post data            
        captchaId: captchaId
      };

      // post both the form-data and captcha data to the backend
      $http({
        method: 'POST',
        url: 'contact.php',
        data: postData
      })
        .then(function (response) {
          if (response.data.success == false) {
            // captcha validation failed; show the error message
            $scope.errorMessages = response.data.errors;
            // call the captcha.reloadImage()
            // in order to generate a new captcha challenge
            captcha.reloadImage();
          } else {
            // captcha validation succeeded; proceed with the workflow
            $window.location.href = '#/contact-success-notify';
          }
        }, function (error) {
          console.log(error.data);
        });
    } else {
      $scope.errorMessages = { formInvalid: 'Please enter valid values.' };
    }
  };

});

Backend: botdetect.xml

The botdetect-captcha-lib/config/botdetect.xml source file contains the following:

  • Captcha style definitions consisting of:
    • The name of a captcha style
    • The ID of an <input> element in which user enters captcha codes
    • Other option settings -- the captcha code length in this particular case
<?xml version="1.0" encoding="UTF-8"?>
<botdetect xmlns="https://captcha.com/schema/php"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="https://captcha.com/schema/php
            https://captcha.com/schema/php/botdetect-4.2.5.xsd">

  <captchaStyles>
    <captchaStyle>
      <name>angularBasicCaptcha</name>
      <userInputID>userCaptchaInput</userInputID>
      <codeLength>3-5</codeLength>
    </captchaStyle>
  </captchaStyles>
</botdetect>

Backend: contact.php

The contact.php source file does the following:

  • Extracts the $userEnteredCaptchaCode and $captchaId values posted from the frontend
  • Validates captchas by calling the Validate($userEnteredCaptchaCode, $captchaId) method
  • Returns the json-formatted captcha validation results to frontend
<?php header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

  require('botdetect-captcha-lib/simple-botdetect.php');

  $postedData = (array) json_decode(file_get_contents('php://input'), true);

  $name = $postedData['name'];
  $email = $postedData['email'];
  $subject = $postedData['subject'];
  $message = $postedData['message'];
  $userEnteredCaptchaCode = $postedData['userEnteredCaptchaCode'];
  $captchaId = $postedData['captchaId'];

  // validate the form data
  $error = array();

  if (!isValidName($name)) {
    $error['name'] = "Name must be at least 3 chars long!";
  }

  if (!isValidEmail($email)) {
    $error['email'] = "Email is invalid!";
  }

  if (!isValidSubject($subject)) {
    $error['subject'] = "Subject must be at least 10 chars long!";
  }

  if (!isValidMessage($message)) {
    $error['message'] = "Message must be at least 10 chars long!";
  }

  // validate the user entered captcha code
  if (!isCaptchaCorrect($userEnteredCaptchaCode, $captchaId)) {
    $error['userEnteredCaptchaCode'] = "CAPTCHA validation failed!";
    // TODO: consider logging the attempt
  }

  if (empty($error)) {
    // TODO: all validations succeeded; execute the protected action
    // (send email, write to database, etc...)
  }

  // return the json string with the validation result to the frontend
  $result = array('success' => empty($error), 'errors' => $error);
  echo json_encode($result, true); exit;
}

// the captcha validation function
function isCaptchaCorrect($userEnteredCaptchaCode, $captchaId) {
  // create a captcha instance to be used for the captcha validation
  $captcha = new SimpleCaptcha();
  // execute the captcha validation
  return $captcha->Validate($userEnteredCaptchaCode, $captchaId);
}

function isValidName($name) {
  if($name == null) {
    return false;
  }
  return (strlen($name) >= 3);
}

function isValidEmail($email) {
  if($email == null) {
    return false;
  }

  return preg_match("/^[\\w-_\\.+]*[\\w-_\\.]\\@([\\w]+\\.)+[\\w]+[\\w]$/", $email, $matches);
}

function isValidSubject($subject) {
  if($subject == null) {
    return false;
  }

  return (strlen($subject) > 9) && (strlen($subject) < 255);
}

function isValidMessage($message) {
  if($message == null) {
    return false;
  }

  return (strlen($message) > 9) && (strlen($message) < 255);
}

Please Note

The new experimental Simple API is still work-in-progress. This Integration Guide & Examples require you to have the Angular Captcha Module v3.7.4 or AngularJS Captcha Module v1.6.1 on frontend, and one of the following BotDetect CAPTCHA releases on backend: ASP.NET v4.4.2+, Java v4.0.Beta3.7+, PHP v4.2.5+.

Current BotDetect Versions