ASP.NET MVC 3.0 Application Template CAPTCHA C# Code Sample (BotDetect v3.0; deprecated)
The ASP.NET MVC 3.0 application template Captcha sample project shows how to use the BotDetect CAPTCHA MvcCaptcha
control in ASP.NET MVC 3.0 web applications.
First Time Here?
Check the BotDetect ASP.NET MVC Captcha Quickstart for key integration steps.
Starting with the default ASP.NET MVC 3.0 Visual Studio 2010 project template, the sample includes all code required to add CAPTCHA validation to the Account
controller Register
action.
The sample remembers when the CAPTCHA is successfully solved within a single registration, and doesn't display it again if there are errors with other form values (the username, for example).
You can get a new CAPTCHA using the Register
action link in the top right corner of the page, and see the explanation in the Account
controller code.
→ ASP.NET MVC version:
- ASP.NET MVC 5.0
- ASP.NET MVC 4.0
- ASP.NET MVC 3.0
- ASP.NET MVC 2.0
- MVC 1.0
→ .NET programming language:
- C#
- VB.NET
Installed Location
By default, the .NET 4.0 C# version of the ASP.NET MVC 3.0 application template Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.0\WebApp\AspNetMVC30CaptchaSample\CSharp
You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 4.0 Web Applications > Run
- Models\AccountModels.cs
- Views\Account\Register.cshtml
- Views\Shared\_Layout.cshtml
- Controllers\AccountController.cs
- CaptchaHelper.cs
- Global.asax.cs
- Web.config
Models\AccountModels.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Web.Mvc; using System.Web.Security; namespace AspNetMvc30CaptchaSampleCSharp.Models { public class ChangePasswordModel { [Required] [DataType(DataType.Password)] [Display(Name = "Current password")] public string OldPassword { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public class LogOnModel { [Required] [Display(Name = "User name")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } } public class RegisterModel { [Required] [Display(Name = "User name")] public string UserName { get; set; } [Required] [DataType(DataType.EmailAddress)] [Display(Name = "Email address")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } // add Captcha code as a field in the protected action Model [Required(ErrorMessage = "Retyping the characters from the picture is required.")] [Display(Name = "Please retype the characters from the picture")] public string CaptchaCode { get; set; } } }
Since we want to include Captcha validation in the Register action, we add a string field representing the Captcha code to the related RegisterModel
class declaration.
The [Required]
attribute specifies that the Captcha code input field cannot be left blank (and which error message to display if it is), while the [Display]
attribute declares the text description used to prompt users to type the Captcha code.
Views\Account\Register.cshtml
@model AspNetMvc30CaptchaSampleCSharp.Models.RegisterModel @using BotDetect.Web.UI.Mvc; @{ ViewBag.Title = "Register"; } @* add BotDetect header includes *@ @section BotDetectStyles { <link href="@BotDetect.Web.CaptchaUrls.Absolute.LayoutStyleSheetUrl" rel="stylesheet" type="text/css" /> } <h2>Create a New Account</h2> <p> Use the form below to create a new account. </p> <p> Passwords are required to be a minimum of @Membership. MinRequiredPasswordLength characters in length. </p> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min. js")" type="text/javascript"></script> @using (Html.BeginForm()) { <p> @Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.") </p> <div> <fieldset id="RegisterFields"> <legend>Account Information</legend> <div class="editor-label"> @Html.LabelFor(m => m.UserName) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.UserName) @Html.ValidationMessageFor(m => m.UserName) </div> <div class="editor-label"> @Html.LabelFor(m => m.Email) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.Email) @Html.ValidationMessageFor(m => m.Email) </div> <div class="editor-label"> @Html.LabelFor(m => m.Password) </div> <div class="editor-field"> @Html.PasswordFor(m => m.Password) @Html.ValidationMessageFor(m => m.Password) </div> <div class="editor-label"> @Html.LabelFor(m => m.ConfirmPassword) </div> <div class="editor-field"> @Html.PasswordFor(m => m.ConfirmPassword) @Html.ValidationMessageFor(m => m.ConfirmPassword) </div> @* add Captcha validation controls to the protected action View *@ @{ MvcCaptcha registrationCaptcha = CaptchaHelper. GetRegistrationCaptcha(); } @if (!registrationCaptcha.IsSolved) { <div class="editor-label"> @Html.LabelFor(m => m.CaptchaCode) @Html.Captcha(registrationCaptcha) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.CaptchaCode) @Html.ValidationMessageFor(m => m.CaptchaCode) </div> } <p style="width: 210px; text-align: right;"> <input type="submit" value="Register" /> </p> </fieldset> </div> }
Following ASP.NET MVC 3.0 Captcha View instructions, the BotDetectStyles
section includes required BotDetect stylesheets in the generated page header.
To keep View code simple, we delegate MvcCaptcha
instance creation to a helper class, and use it along with Model fields to generate the required markup using straightforward HtmlHelper
calls.
Since we want to avoid re-displaying the Captcha challenge to users after they solve it, we only call the markup generation code if the IsSolved
property is not set by a previous successful Captcha validation.
Views\Shared\_Layout.cshtml
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script> @* allow BotDetect stylesheet includes in selected Views *@ @RenderSection("BotDetectStyles", required: false) </head> <body> <div class="page"> <header> <div id="title"> <h1>BotDetect CAPTCHA ASP.NET MVC 3.0 Sample</h1> </div> <div id="logindisplay"> @Html.Partial("_LogOnPartial") </div> <nav> <ul id="menu"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> </ul> </nav> </header> <section id="main"> @RenderBody() </section> <footer> </footer> </div> </body> </html>
To allow easy inclusion of BotDetect stylesheets into Views which require them, the BotDetectStyles
section was declared in the layout template header.
Controllers\AccountController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using System.Web.Security; using AspNetMvc30CaptchaSampleCSharp.Models; using BotDetect.Web.UI.Mvc; namespace AspNetMvc30CaptchaSampleCSharp.Controllers { public class AccountController : Controller { // // GET: /Account/LogOn public ActionResult LogOn() { return View(); } // // POST: /Account/LogOn [HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (Membership.ValidateUser(model.UserName, model. Password)) { FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl. StartsWith("/\\")) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/LogOff public ActionResult LogOff() { FormsAuthentication.SignOut(); return RedirectToAction("Index", "Home"); } // // GET: /Account/Register public ActionResult Register() { // when the View is accessed directly and not posted, we // clear any remembered CAPTCHA solving state. // The users only have to solve the CAPTCHA once within a // single registration, but if they reload the Register // page later, it is shown again. // Otherwise, they could register an unlimited number of // accounts within a single Session after solving the // CAPTCHA only once. MvcCaptcha.ResetCaptcha("RegistrationCaptcha"); return View(); } // // POST: /Account/Register [HttpPost] [CaptchaValidation("CaptchaCode", "RegistrationCaptcha", "Your input doesn't match displayed characters.")] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user MembershipCreateStatus createStatus; Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus); if (createStatus == MembershipCreateStatus.Success) { FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */); return RedirectToAction("Index", "Home"); } else { ModelState.AddModelError("", ErrorCodeToString( createStatus)); } } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/ChangePassword [Authorize] public ActionResult ChangePassword() { return View(); } // // POST: /Account/ChangePassword [Authorize] [HttpPost] public ActionResult ChangePassword(ChangePasswordModel model) { if (ModelState.IsValid) { // ChangePassword will throw an exception rather // than return false in certain failure scenarios. bool changePasswordSucceeded; try { MembershipUser currentUser = Membership.GetUser( User.Identity.Name, true /* userIsOnline */); changePasswordSucceeded = currentUser. ChangePassword(model.OldPassword, model. NewPassword); } catch (Exception) { changePasswordSucceeded = false; } if (changePasswordSucceeded) { return RedirectToAction("ChangePasswordSuccess"); } else { ModelState.AddModelError("", "The current password is incorrect or the new password is invalid."); } } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/ChangePasswordSuccess public ActionResult ChangePasswordSuccess() { return View(); } #region Status Codes private static string ErrorCodeToString(MembershipCreateStatus createStatus) { // See http://go.microsoft.com/fwlink/?LinkID=177550 for // a full list of status codes. switch (createStatus) { case MembershipCreateStatus.DuplicateUserName: return "User name already exists. Please enter a different user name."; case MembershipCreateStatus.DuplicateEmail: return "A user name for that e-mail address already exists. Please enter a different e-mail address."; case MembershipCreateStatus.InvalidPassword: return "The password provided is invalid. Please enter a valid password value."; case MembershipCreateStatus.InvalidEmail: return "The e-mail address provided is invalid. Please check the value and try again."; case MembershipCreateStatus.InvalidAnswer: return "The password retrieval answer provided is invalid. Please check the value and try again."; case MembershipCreateStatus.InvalidQuestion: return "The password retrieval question provided is invalid. Please check the value and try again."; case MembershipCreateStatus.InvalidUserName: return "The user name provided is invalid. Please check the value and try again."; case MembershipCreateStatus.ProviderError: return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator."; case MembershipCreateStatus.UserRejected: return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator."; default: return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator."; } } #endregion } }
To add Captcha validation to the Register action Controller code, we perform a single line of setup in the Register
action when the user GETs it, and execute the [CaptchaValidation]
filter when the user POSTs it.
The filter attribute will automatically add the appropriate ModelState
error if the Captcha code input doesn't match the code displayed to the user in the Captcha picture.
CaptchaHelper.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using BotDetect; using BotDetect.Web; using BotDetect.Web.UI; using BotDetect.Web.UI.Mvc; public class CaptchaHelper { public static MvcCaptcha GetRegistrationCaptcha() { // create the control instance MvcCaptcha registrationCaptcha = new MvcCaptcha( "RegistrationCaptcha"); registrationCaptcha.UserInputClientID = "CaptchaCode"; // all Captcha properties are set in this event handler registrationCaptcha.InitializedCaptchaControl += new EventHandler<InitializedCaptchaControlEventArgs>( RegistrationCaptcha_InitializedCaptchaControl); return registrationCaptcha; } // event handler used for Captcha control property randomization static void RegistrationCaptcha_InitializedCaptchaControl( object sender, InitializedCaptchaControlEventArgs e) { if (e.CaptchaId != "RegistrationCaptcha") { return; } CaptchaControl registrationCaptcha = sender as CaptchaControl; // fixed Captcha settings registrationCaptcha.ImageSize = new System.Drawing.Size(200, 50); registrationCaptcha.CodeLength = 4; // randomized Captcha settings registrationCaptcha.ImageStyle = CaptchaRandomization. GetRandomImageStyle(); registrationCaptcha.SoundStyle = CaptchaRandomization. GetRandomSoundStyle(); } }
Captcha instance creation and property setting is encapsulated in this simple helper class. This separation allows application Views to stay simple regardless of the amount of Captcha customization chosen.
Global.asax.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace AspNetMvc30CaptchaSampleCSharp { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters( GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); // BotDetect requests must not be routed routes.IgnoreRoute("{*botdetect}", new { botdetect = @"(.*) BotDetectCaptcha\.ashx" }); // Set the Register action as the sample project default, // since it includes the Captcha control and we want to show it routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Account", action = "Register", id = UrlParameter.Optional } // Parameter defaults ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } } }
Since all BotDetect requests are handled by the BotDetect HttpHandler
, ASP.NET Url Routing must be configured to ignore BotDetect requests, which can be achieved by a single line of code in Global.asax
code-behind.
Web.config
<?xml version="1.0"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=152368 --> <configuration> <connectionStrings> <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf; User Instance=true" providerName="System.Data.SqlClient" /> </connectionStrings> <appSettings> <add key="webpages:Version" value="1.0.0.0"/> <add key="ClientValidationEnabled" value="true"/> <add key="UnobtrusiveJavaScriptEnabled" value="true"/> </appSettings> <system.web> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation> <authentication mode="Forms"> <forms loginUrl="~/Account/LogOn" timeout="2880" /> </authentication> <membership> <providers> <clear/> <add name="AspNetSqlMembershipProvider" type="System.Web. Security.SqlMembershipProvider" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" /> </providers> </membership> <profile> <providers> <clear/> <add name="AspNetSqlProfileProvider" type="System.Web.Profile. SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" /> </providers> </profile> <roleManager enabled="false"> <providers> <clear/> <add name="AspNetSqlRoleProvider" type="System.Web.Security. SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" /> <add name="AspNetWindowsTokenRoleProvider" type="System.Web. Security.WindowsTokenRoleProvider" applicationName="/" /> </providers> </roleManager> <!-- make sure Session State is enabled --> <pages enableSessionState="true"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> <add namespace="System.Web.WebPages" /> </namespaces> </pages> <!-- configure Session State for BotDetect use --> <sessionState mode="InProc" cookieless="AutoDetect" timeout="20" sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, BotDetect"/> <httpHandlers> <!-- register HttpHandler used for BotDetect Captcha requests --> <add verb="GET" path="BotDetectCaptcha.ashx" type="BotDetect.Web.CaptchaHandler, BotDetect"/> </httpHandlers> </system.web> <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules runAllManagedModulesForAllRequests="true"/> <handlers> <!-- register HttpHandler used for BotDetect Captcha requests --> <remove name="BotDetectCaptchaHandler"/> <add name="BotDetectCaptchaHandler" preCondition="integratedMode" verb="GET" path="BotDetectCaptcha.ashx" type="BotDetect.Web.CaptchaHandler, BotDetect"/> </handlers> </system.webServer> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0. 0.0" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
The application's web.config
file includes the standard BotDetect HttpHandler
and Session state configuration elements.
Please Note
The information on this page is out of date and applies to a deprecated version of BotDetect™ CAPTCHA (v3.0).
An up-to-date equivalent page for the latest BotDetect Captcha release (v4) is BotDetect v4 Captcha documentation index.
General information about the major improvements in the current BotDetect release can be found at the What's New in BotDetect v4.0 page.
Current BotDetect Versions
-
BotDetect ASP.NET CAPTCHA
2019-07-22v4.4.2 -
BotDetect Java CAPTCHA
2019-07-22v4.0.Beta3.7 -
BotDetect PHP CAPTCHA
2019-07-22v4.2.5