ASP.NET MVC 4.0 Application Template CAPTCHA C# Razor Code Sample (BotDetect v3.0; deprecated)
The ASP.NET MVC 4.0 application template Captcha C# Razor sample project shows how to use the BotDetect CAPTCHA MvcCaptcha
control in ASP.NET MVC 4.0 C# web applications using Razor views.
First Time Here?
Check the BotDetect ASP.NET MVC Captcha Quickstart for key integration steps.
Starting with the default ASP.NET MVC 4.0 Internet Application Visual Studio 2012 project template (File > New Project > Installed > Templates > Visual C# > Web > ASP.NET MVC 4 Web Application > Template: Internet Application, View Engine: Razor
), the sample includes all code required to add CAPTCHA validation to the Account
controller Register
action (Views\Account\Register.cshtml
and Controllers\AccountController.cs
).
The sample also shows how to complement server-side CAPTCHA validation with client-side Ajax CAPTCHA validation using ASP.NET MVC 4.0 unobtrusive validation (based on jQuery) applied to all form fields (Scripts\captcha.validate.js
).
→ 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
→ View engine:
- ASPX
- Razor
Installed Location
By default, the .NET 4.5 C# Razor version of the ASP.NET MVC 4.0 application template Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.5\WebApp\AspNetMvc40CaptchaSample\Razor\CSharp
You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 4.5 Web Applications > Run
- Views\Account\Register.cshtml
- Controllers\AccountController.cs
- App_Code\CaptchaHelper.cs
- Scripts\captcha.validate.js
- Views\Shared\_Layout.cshtml
- App_Start\RouteConfig.cs
- Web.config
Views\Account\Register.cshtml
@model AspNetMvc40CaptchaSampleRazorCSharp.Models.RegisterModel @* namespaces needed to access BotDetect members and the CaptchaHelper class *@ @using BotDetect.Web.UI.Mvc; @using AspNetMvc40CaptchaSampleRazorCSharp.App_Code; @{ ViewBag.Title = "Register"; } @* include BotDetect layout stylesheet in page <head> *@ @section HeadIncludes { <link href="@BotDetect.Web.CaptchaUrls.Absolute.LayoutStyleSheetUrl" rel="stylesheet" type="text/css" /> } <hgroup class="title"> <h1>@ViewBag.Title.</h1> <h2>Create a new account.</h2> </hgroup> @using (Html.BeginForm("Register", "Account")) { @Html.AntiForgeryToken() @Html.ValidationSummary() <fieldset class="register"> <legend>Registration Form</legend> <ol> <li> @Html.LabelFor(m => m.UserName) @Html.TextBoxFor(m => m.UserName) </li> <li> @Html.LabelFor(m => m.Password) @Html.PasswordFor(m => m.Password) </li> <li> @Html.LabelFor(m => m.ConfirmPassword) @Html.PasswordFor(m => m.ConfirmPassword) </li> @* showing Captcha on the form: add Captcha validation controls to the protected action View, but only if the Captcha hasn't already been solved *@ @{ MvcCaptcha registrationCaptcha = CaptchaHelper.GetRegistrationCaptcha(); } @if (!registrationCaptcha.IsSolved) { <li> @Html.Label("Retype the code from the picture") @Html.Captcha(registrationCaptcha) @Html.TextBox("CaptchaCode", null, new { @class = "captchaVal" }) </li> } </ol> <input type="submit" value="Register" /> </fieldset> } @section Scripts { @Scripts.Render("~/bundles/jqueryval") @* client-side Captcha validation script inlcude *@ @Scripts.Render("~/Scripts/captcha.validate.js") }
To display Captcha protection on the Register View, we first ensure we can use BotDetect members and the application code by using
the relevant namespaces.
Then, we include the BotDetect layout stylesheet in the page <head>
(through the associated HeadIncludes
section), to make Captcha elements display correctly.
To reduce the amount of code added to the View and make it clearer, we create the registrationCaptcha
object (an instance of the BotDetect MvcCaptcha
class) using the CaptchaHelper
.
When we have the registrationCaptcha
object, displaying the Captcha is as easy as calling the Html.Captcha()
helper with it as a parameter.
We also add a Html.Label()
and a Html.TextBox()
to complete the Captcha elements on the form; note that the textbox has it's CSS class set to captchaVal
– we'll use this class to dynamically add unobtrusive jQuery validation of the user's Captcha input.
Note that we add Captcha fields to the form only if the IsSolved
flag of the Captcha object hasn't been set. This is a small usability improvement for users who don't have JavaScript enabled: if they solve the Captcha correctly but validation of other form fields fails, they won't have to solve the Captcha again (since they have already proven to be a real visitor instead of a bot). Of course, if this is not something you are concerned about, you can simply display the Captcha without this check.
The client-side Captcha validation rules are defined in a JavaScript file called captcha.validate.js
, which we include in the ScriptsSection
content placeholder after all other jQuery validation scripts in the jqueryval
bundle.
Controllers\AccountController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Transactions; using System.Web; using System.Web.Mvc; using System.Web.Security; using DotNetOpenAuth.AspNet; using Microsoft.Web.WebPages.OAuth; using WebMatrix.WebData; using AspNetMvc40CaptchaSampleRazorCSharp.Models; using BotDetect.Web.UI.Mvc; namespace AspNetMvc40CaptchaSampleRazorCSharp.Controllers { [Authorize] public class AccountController : Controller { […] // // POST: /Account/Register [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] [CaptchaValidation("CaptchaCode", "RegistrationCaptcha", "Your input doesn't match displayed characters")] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user try { WebSecurity.CreateUserAndAccount(model.UserName, model.Password); WebSecurity.Login(model.UserName, model.Password); // Reset CAPTCHA status after successful account // registration. 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 RedirectToAction("Index", "Home"); } catch (MembershipCreateUserException e) { ModelState.AddModelError("", ErrorCodeToString( e.StatusCode)); } } // If we got this far, something failed, redisplay form return View(model); } […]
Above we're showing only the part of AccountController
code tied to Captcha validation functionality, since that's what we're interested in.
After we've included the BotDetect.Web.UI.Mvc
namespace, we just need to add the CaptchaValidation
attribute to the method processing Register form submissions. The attribute takes three parameters:
- the ID of the textbox containing the user's Captcha code input (which we named
CaptchaCode
on the Register form), - the ID of the Captcha instance we're validating (which we set to
RegistrationCaptcha
in theCaptchaHelper class
), and - the error message to be shown when Captcha validation fails.
When the Captcha validation action filter attribute has been added, the Captcha validation will trigger every time the Register form is submitted, and will automatically add a Model error with the error message configured above when Captcha validation fails. So we don't need to check the Captcha validation status separately from the validation status of Model fields – checking if (ModelState.IsValid)
is enough.
The only other change we make is calling MvcCaptcha.ResetCaptcha()
after the user account has been successfully created. As the code comment says, we do this to ensure the user is shown a new Captcha if they open the Register form again in the same session. This is tied to the IsSolved
check we use on the form, so if you decide not to use that check, resetting the Captcha status is unnecessary.
App_Code\CaptchaHelper.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using BotDetect.Web.UI.Mvc; namespace AspNetMvc40CaptchaSampleRazorCSharp.App_Code { public static class CaptchaHelper { public static MvcCaptcha GetRegistrationCaptcha() { // create the control instance MvcCaptcha registrationCaptcha = new MvcCaptcha( "RegistrationCaptcha"); // set up client-side processing of the Captcha code input textbox registrationCaptcha.UserInputClientID = "CaptchaCode"; // Captcha settings registrationCaptcha.ImageSize = new System.Drawing.Size(200, 50); registrationCaptcha.CodeLength = 4; return registrationCaptcha; } } }
Since there are many properties of the Captcha object you might want to set, we separate MvcCaptcha
instance creation in a class apart from View code. This way we keep the View code short, and can have multiple Captchas in different Views without having to keep Captcha code scattered over each View code.
Each Captcha instance should have a unique ID, which we pass to the MvcCaptcha
constructor (and which is also passed to the CaptchaValidation
attribute in Controller code).
Setting the UserInputClientID
property to the client-side ID of the Captcha code input textbox is necessary to wire-up client-side input and validation functionality.
Scripts\captcha.validate.js
(function () { $.validator.setDefaults({ // only validate fields when the form is submitted: // the Captcha input must only be validated when the whole code string // is typed in, not after each individual character (onkeyup must be // false); onfocusout validation could be left on in more complex // forms, but doesn't fit this sample onkeyup: false, onfocusout: false, // always reload the Captcha image if remote validation failed, // since it will not be usable any more (a failed validation attempt // removes the attempted code for necessary Captcha security showErrors: function (errorMap, errorList) { for (var i = 0; i < errorList.length; i++) { var element = errorList[i].element; var message = errorList[i].message; // check element css class and does the error message match // remote validation failure if (element.className.match(/captchaVal/) && message === this.settings.messages[element.id].remote) { element.Captcha.ReloadImage(); $("form").valid(); } } } }); })(); $(document).ready(function () { // add validation rules by CSS class, so we don't have to know the // exact client id of the Captcha code textbox $(".captchaVal").rules('add', { required: true, remote: $(".captchaVal").get(0).Captcha.ValidationUrl, messages: { required: "Your input doesn't match displayed characters", remote: "Incorrect code, please try again" } }); });
Since ASP.NET MVC unobtrusive validation is based on the jquery.validate.js
jQuery plugin, we add Captcha code textbox client-side validation by extending the validator settings as defined in its API.
We use the CSS class we gave the Captcha code textbox (captchaVal
) to add validation rules making the Captcha code both a required field, and one validated remotely. Since secure Captcha validation can only be performed on the server, we make a validation Ajax request to the validation Url exposed by the client-side Captcha object.
Furthermore, since each random Captcha code can only be attempted once for security reasons, we have to reload the Captcha image whenever Ajax Captcha validation fails (or it would be showing the user a code that cannot be validated anymore, making them always fail the validation even when they retype the Captcha code correctly). We do this in the validator showErrors()
function, after we checked there is a remote Captcha validation error.
For the same reason, the remote Captcha validation only needs to happen when the user has finished retyping the whole Captcha code, and must not trigger after each individual character has been typed. We achieve this by setting the validator onkeyup
setting to false
.
Views\Shared\_Layout.cshtml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>@ViewBag.Title - My ASP.NET MVC Application</title> <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <meta name="viewport" content="width=device-width" /> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") @RenderSection("HeadIncludes", required: false) </head> <body> <header> <div class="content-wrapper"> <div class="float-left"> <p class="site-title">@Html.ActionLink("your logo here", "Index", "Home")</p> </div> <div class="float-right"> <section id="login"> @Html.Partial("_LoginPartial") </section> <nav> <ul id="menu"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home") </li> </ul> </nav> </div> </div> </header> <div id="body"> @RenderSection("featured", required: false) <section class="content-wrapper main-content clear-fix"> @RenderBody() </section> </div> <footer> <div class="content-wrapper"> <div class="float-left"> <p>© @DateTime.Now.Year - My ASP.NET MVC Application </p> </div> </div> </footer> @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) </body> </html>
There is only a single small change made in the main site template: we add a HeadIncludes
content section, which allows us to include the BotDetect layout stylesheet in the page head when it's needed.
App_Start\RouteConfig.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace AspNetMvc40CaptchaSampleRazorCSharp { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); // BotDetect requests must not be routed routes.IgnoreRoute("{*botdetect}", new { botdetect = @"(.*)BotDetectCaptcha\.ashx" }); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Account", action = "Register", id = UrlParameter.Optional } ); } } }
We configure ASP.NET Routing to ignore BotDetect requests, since they do not conform to any MVC-related patterns. The regex defining requests to ignore must match the path configured for the BotDetect HttpHandler
registered in web.config
.
Web.config
<?xml version="1.0" encoding="utf-8"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <configSections> <section name="botDetect" requirePermission="false" type="BotDetect.Configuration.BotDetectConfigurationSection, BotDetect"/> </configSections> <appSettings> <add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <authentication mode="Forms"> <forms loginUrl="~/Account/Login" timeout="2880" /> </authentication> <!-- make sure Session State is enabled --> <pages enableSessionState="true"> <namespaces> <add namespace="System.Web.Helpers" /> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Optimization" /> <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" /> <handlers> <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" /> <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" /> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET, HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0. 30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0, bitness32" responseBufferLimit="0" /> <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET, HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0. 30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0, bitness64" responseBufferLimit="0" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET, HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers. TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> <!-- 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.Helpers" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" /> <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" /> </dependentAssembly> </assemblyBinding> </runtime> <botDetect> <captchaImage> <helpLink enabled="true" mode="image" /> </captchaImage> </botDetect> </configuration>
To allow the application to use BotDetect Captcha protection, we must enable and configure sessionState
(required for BotDetect), and register the BotDetect HttpHandler
in both <system.web><httpHandlers>
and <system.webServer><handlers>
configuration sections.
The <dependentAssembly>
entry for System.Web.Mvc
is also needed to make all ASP.NET MVC dependencies referenced by the BotDetect MVC assembly point to the correct ASP.NET MVC version.
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