Google has released a new version of reCAPTCHA on 3 December. You can indicate you’re not robot by selecting one checkbox. Google has named the new API ‘No CAPTCHA reCAPTCHA’. You can read more about the new version on this blog.
To implement the reCAPTCHA in XForms the rendering needs to be customized. In MVC this can be done by creating a view in the directory /Views/Shared/DisplayTemplates named XForm.cshtml. This view will give full control for rendering a form.
In the below code snippet I’ve customized the rendering for inserting the reCAPTCHA before the submit button. All form fields (textfield, textarea, submit button, etc.) are HtmlFragments objects. To Insert the reCAPTCHA before the submit button check whether the Fragment object is of type SubmitFragment.
<div>
@{
@Html.ValidationSummary()
using (Html.BeginXForm(Model, htmlAttributes: new { @class = "form xform" }))
{
if (Model != null)
{
foreach (HtmlFragment fragment in Model.CreateHtmlFragments())
{
if (fragment is SubmitFragment)
{
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="@ConfigurationManager.AppSettings["ReCaptchaSiteKey"]"></div>
@:</td></tr><tr><td valign="top">
}
@Html.Fragment(fragment)
}
}
}
}
</div>
The implementation of reCAPTCHA is quite easy, first you need to register for obtaining a site key and secret value. After that the below HTML snippet can be added to the view.
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="sitekey"></div>
Now the only thing that needs to be done is the validation of the form. When rendering the XForm page property the XFormParameters object can be passed. This object contains information which controller and actions (success, failed and postaction) are called when submitting the form.
@Html.PropertyFor(x => x.CurrentPage.Form, (new { XFormParameters = new XFormParameters() { SuccessAction = "Success", FailedAction = "Failed", PostAction = "DoSubmit" } }))
Now the DoSubmit action is called when the form is submitted on the page. Here the validation of reCAPTCHA can be implemented. To validate if the form has been submitted by a human a request to the reCAPTCHA API needs to be performed. Two parameters should be passed, the site secret value and the post variable g-recaptcha-response that’s available when the form has been submitted.
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Success(StartPage currentPage, XFormPostedData xFormPostedData)
{
return View("Index", PageViewModel.Create(currentPage));
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Failed(StartPage currentPage, XFormPostedData xFormPostedData)
{
return View("Index", PageViewModel.Create(currentPage));
}
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult DoSubmit(XFormPostedData xFormpostedData)
{
ValidateReCaptcha();
return new XFormPageUnknownActionHandler().HandleAction(this);
}
public void ValidateReCaptcha()
{
var result = Request.Form["g-recaptcha-response"];
var url = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
ConfigurationManager.AppSettings["ReCaptchaSiteSecret"],
result);
var request = HttpWebRequest.Create(url);
var objStream = request.GetResponse().GetResponseStream();
if (objStream != null)
{
var objReader = new StreamReader(objStream);
var googleResults = objReader.ReadToEnd();
var recaptchaResult = JsonConvert.DeserializeObject<ReCaptchaResponse>(googleResults);
if (!recaptchaResult.Success)
{
ModelState.AddModelError("ReCaptcha", "ReCaptcha is not valid");
}
}
}
The API returns a JSON result, this will indicates if the validation succeeded. I created an object that represents the JSON:
public class ReCaptchaResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("error-codes")]
public string[] ErrorCodes { get; set; }
}
When the validation don’t succeed an error message is added to the ModelState.
The result of the implementation in XForms: