Jonathan Wood
Jonathan Wood

Reputation: 67195

Using reCAPTCHA with ASP.NET MVC

I've used reCAPTCHA many times in my WebForms applications. Now I'd like to add it to an ASP.NET MVC application.

I found what appears to be some good code in RecaptchaControlMvc but, incredibly, I haven't been able to find a single paragraph or example on how to use this control.

I've posted in the Google reCAPTCHA group but it's dead.

Can anyone point me to an example that uses this control, a paragraph about how to use it, or suggest an alternative?

Note: I know there are similar questions on stackoverflow, but none that I've found discuss how to use this control.

Upvotes: 1

Views: 8523

Answers (3)

DMObjects
DMObjects

Reputation: 11

This is How I do it with ASP.Net MVC and ReCaptcha 3.

  1. In Visual Studio 2015 or 2017, create a new ASP.NET MVC project and set the target .NET Framework to 4.6.2.
  2. Create a descriptive name for your ASP.NET MVC project (e.g. ASPNetMVCWithReCaptcha3).
  3. Build your application and make sure it compiles and is error free.
  4. In Web.Config, create two new keys under and copy/paste the site key and secret key from the text editor where you saved these references.
<appSettings>
    <add key="reCaptchaSiteKey" value="site_key" />
    <add key="reCaptchaSecretKey" value="secret_key" />
</appSettings>
  1. In the "Models" folder, create a new class "ReCaptchaForm.cs" and copy/paste the following lines of code.
namespace ASPNetMVCWithReCaptcha3.Models
{
    public class ReCaptchaForm
    {
        public string Message { get; set; }
    }
}
  1. Create a new "Classes" folder in the project.
  2. In the "Classes" folder, create a new class "ReCaptcha.cs" and copy/paste the following lines of code:
using System;
using System.Web;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Net.Http;
using System.Configuration;

namespace ASPNetMVCWithReCaptcha3.Classes
{
}
  1. Add the following lines inside the namespace

    • Add a class "GoogleReCaptchaVariables" that will store variables needed to render ReCaptcha.
public static class GoogleReCaptchaVariables
{
     public static string ReCaptchaSiteKey = ConfigurationManager.AppSettings["reCaptchaSiteKey"]?.ToString() ?? string.Empty;
     public static string ReCaptchaSecretKey = ConfigurationManager.AppSettings["reCaptchaSecretKey"]?.ToString() ?? string.Empty;
     public static string InputName = "g-recaptcha-response";
}
  • Create a helper class to render hidden input for response token.
public static class ReCaptchaHelper
{
     public static IHtmlString ReCaptchaHidden(this HtmlHelper helper)
     {
          var mvcHtmlString = new TagBuilder("input")
          {
               Attributes =
               {
                    new KeyValuePair<string, string>("type", "hidden"),
                    new KeyValuePair<string, string>
                           ("id", GoogleReCaptchaVariables.InputName),
                    new KeyValuePair<string, string>
                           ("name", GoogleReCaptchaVariables.InputName)
               }
          };
          string renderedReCaptchaInput = 
                   mvcHtmlString.ToString(TagRenderMode.Normal);
          return MvcHtmlString.Create($"{renderedReCaptchaInput}");
     }

     public static IHtmlString ReCaptchaJS
             (this HtmlHelper helper, string useCase = "homepage")
     {
          string reCaptchaSiteKey = GoogleReCaptchaVariables.ReCaptchaSiteKey;
          string reCaptchaApiScript = "<script 
          src='https://www.google.com/recaptcha/api.js?render=" + 
          reCaptchaSiteKey + "'></script>;";
          string reCaptchaTokenResponseScript = "<script>
          $('form').submit(function(e) { e.preventDefault(); 
          grecaptcha.ready(function() { grecaptcha.execute('" + 
          reCaptchaSiteKey + "', {action: '" + useCase + 
          "'}).then(function(token) { $('#" + 
          GoogleReCaptchaVariables.InputName + "').val(token); 
          $('form').unbind('submit').submit(); }); }); }); </script>;";
          return MvcHtmlString.Create
                 ($"{reCaptchaApiScript}{reCaptchaTokenResponseScript}");
     }
}
  • Add another helper class that renders "span" tag to display error message on failed ReCaptcha verification.
public static IHtmlString ReCaptchaValidationMessage
             (this HtmlHelper helper, string errorText = null)
{
     var invalidReCaptchaObj = 
            helper.ViewContext.Controller.TempData["InvalidCaptcha"];
     var invalidReCaptcha = invalidReCaptchaObj?.ToString();
     if (string.IsNullOrWhiteSpace(invalidReCaptcha)) 
                          return MvcHtmlString.Create("");
     var buttonTag = new TagBuilder("span")
     {
          Attributes = {
               new KeyValuePair<string, string>("class", "text-danger")
          },
          InnerHtml = errorText ?? invalidReCaptcha
     };
     return MvcHtmlString.Create(buttonTag.ToString(TagRenderMode.Normal));
}
  • Next, create an attribute "ValidateReCaptchaAttribute" to process the API call. Add an internal class "ResponseToken" to store response data. Then, implement a validation logic to show error message on failed verification.
public class ValidateReCaptchaAttribute : ActionFilterAttribute
{
     public override void OnActionExecuting(ActionExecutingContext filterContext)
     {
          string reCaptchaToken = 
          filterContext.HttpContext.Request.Form[GoogleReCaptchaVariables.InputName];
          string reCaptchaResponse = ReCaptchaVerify(reCaptchaToken);
          ResponseToken response = new ResponseToken();
          if (reCaptchaResponse != null)
          {
               response = Newtonsoft.Json.JsonConvert.DeserializeObject
                          (reCaptchaResponse);
          }
          if (!response.Success)
          {
               AddErrorAndRedirectToGetAction(filterContext);
          }
          base.OnActionExecuting(filterContext);
     }

     public string ReCaptchaVerify(string responseToken)
     {
          const string apiAddress = 
                   "https://www.google.com/recaptcha/api/siteverify";
          string recaptchaSecretKey = GoogleReCaptchaVariables.ReCaptchaSecretKey;
          string urlToPost = $"{apiAddress}
          ?secret={recaptchaSecretKey}&response={responseToken}";
          string responseString = null;
          using (var httpClient = new HttpClient())
          {
               try
               {
                   responseString = httpClient.GetStringAsync(urlToPost).Result;
               }
               catch
               {
                   //Todo: Error handling process goes here
               }
           }
           return responseString;
      }

      private static void AddErrorAndRedirectToGetAction
      (ActionExecutingContext filterContext, string message = null)
      {
           filterContext.Controller.TempData["InvalidCaptcha"] = 
           message ?? "Invalid Captcha! The form cannot be submitted.";
           filterContext.Result = 
                 new RedirectToRouteResult(filterContext.RouteData.Values);
      }

      internal class ResponseToken
      {
           public bool Success { get; set; }
           public float Score { get; set; }
           public string Action { get; set; }
           public DateTime Challenge_TS { get; set; }
           public string HostName { get; set; }
           public List ErrorCodes { get; set; }
      }
 }
  • In the controller, create a postback action that implements the [ValidateReCaptcha] attribute.
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateReCaptcha]
public ActionResult Index(ReCaptchaForm form)
{
     return View(form);
}
  • On your controller's view, add the following lines to render the form, message input and submit bottom.
@model ASPNetMVCWithReCaptcha3.Models.ReCaptchaForm
@using ASPNetMVCWithReCaptcha3.Classes;
@{
    ViewBag.Title = "ReCaptcha Form";
}
@using (Html.BeginForm())
{
     @Html.AntiForgeryToken()
     @Html.LabelFor(model => model.Message)
     @Html.TextAreaFor(model => model.Message, new { @class = "form-control" })
     @Html.ReCaptchaValidationMessage()
     @Html.ReCaptchaHidden()
     @Html.ReCaptchaJS()
     <button type="submit" class="btn btn-primary">Send Message</button>
}
  1. Build the project and make sure it is error free. As a result, the form should submit if ReCaptcha API returns a successful response. In contrast, the error message will show up on the bottom of message input if a failed response has been returned by the API.

Upvotes: 1

Sourav Mondal
Sourav Mondal

Reputation: 435

I can provide you an easy alternative method to use google recaptcha. Here you can find the complete reference about Google new reCAPTCHA using asp.net mvc

First all of you need to Sign up & Generate Google reCAPTCHA API. Go to http://www.google.com/recaptcha then click on the top right corner Get reCAPTCHA button

Second, Write html code in your view. Here replace text "Your sitekey here"

@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<div>
    @using (Html.BeginForm("FormSubmit", "Home", FormMethod.Post))
{
    <div class="g-recaptcha" data-sitekey="Your sitekey here"></div>
    <input type="submit" value="Submit" />
}
</div>
<span style="display:inline-block; font-size:20px;margin:20px 0;padding:20px;border:1px solid #D3D3D3">
    @ViewBag.Message
</span>
<script src='https://www.google.com/recaptcha/api.js' type="text/javascript"></script>

3rd and last, Write action code for validate google reCaptcha

[HttpPost]
public ActionResult FormSubmit()
{
    //Validate Google recaptcha here
    var response = Request["g-recaptcha-response"];
    string secretKey = "Your secret here";
    var client = new WebClient();
    var result = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, response));
    var obj = JObject.Parse(result);
    var status = (bool)obj.SelectToken("success");
    ViewBag.Message = status ? "Google reCaptcha validation success" : "Google reCaptcha validation failed";

    //When you will post form for save data, you should check both the model validation and google recaptcha validation
    //EX.
    /* if (ModelState.IsValid && status)
    {

    }*/

    //Here I am returning to Index page for demo perpose, you can use your view here
    return View("Index");
}

Upvotes: 1

Nick
Nick

Reputation: 2907

Some code here

You add the attribute like this:

[CaptchaValidator]  
[AcceptVerbs( HttpVerbs.Post )]  
public ActionResult SubmitForm( Int32 id, bool captchaValid )  
{  
.. Do something here  
}  

You render the captcha in your view:

<%= Html.GenerateCaptcha() %> 

which is something like this:

public static string GenerateCaptcha( this HtmlHelper helper )  
{           
    var captchaControl = new Recaptcha.RecaptchaControl  
            {  
                    ID = "recaptcha",  
                    Theme = "blackglass",  
                PublicKey = -- Put Public Key Here --,  
                        PrivateKey = -- Put Private Key Here --  
            };  

    var htmlWriter = new HtmlTextWriter( new StringWriter() );  
    captchaControl.RenderControl(htmlWriter);  
    return htmlWriter.InnerWriter.ToString();  
}  

Upvotes: 2

Related Questions