Using Captcha with AJAX Controller

Jan 24, 2013 at 8:50 PM

Hi!

Thanks for the piece of useful software!

But I've got a problem with it. I'm trying to use it with a AJAX form. There's no info on such usage in documentation, plus i'm quite new to mvc, my first project. Any advice on how to use it within an AJAX controller, which returns a JSON object with Message and IsSuccesful props?

An example of the form is here: outstaff-service.ru. (in Russian).

Coordinator
Jan 26, 2013 at 3:44 AM
Edited Jan 26, 2013 at 3:50 AM

Hi, there!

Yes, sure you can use CaptchaMvc in an ajax form. In order to do this you have to put your form in a partial view.

 Example:

@using CaptchaMvc.HtmlHelpers
@model GettingStarted.Controllers.Model


@Html.ValidationSummary(true)

<div class="editor-label">
    @Html.LabelFor(model => model.Test)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Test)
    @Html.ValidationMessageFor(model => model.Test)
</div>

<div>
    @Html.Captcha(5)
</div>

Next make ajax form in view using the partial view.

Example:

@{
    ViewBag.Title = "PartialViewExample";
}

<script>
    $(function() {

        $("form.ajax").submit(function() {
            $.post($(this).attr("action"), $(this).serialize(), function(response) {
                $("#update").html(response);
            });
            return false;
        });
    });
</script>

@using (Html.BeginForm("AjaxForm", "Home", FormMethod.Post, new {@class = "ajax"}))
{
    <div id="update">
        @Html.Partial("Home/_PartialView")
    </div>
    <p>
        <input type="submit" />
    </p>
}

And then add this code to a controller action:

        public ActionResult AjaxForm()
        {
            return View(new Model());
        }

        [HttpPost, CaptchaVerify("Captcha is not valid")]
        public ActionResult AjaxForm(Model model)
        {
            if (ModelState.IsValid)
            {
                ModelState.Clear();
                TempData["Message"] = "Message: captcha is valid.";
                if (Request.IsAjaxRequest())
                    return PartialView("Home/_PartialView");
                return View(model);
            }

            TempData["ErrorMessage"] = "Error: captcha is not valid.";
            if (Request.IsAjaxRequest())
                return PartialView("Home/_PartialView", model);
            return View(model);
        }

If you have any questions write, and I try to answer.

Regards,
Vyacheslav Volkov

Jan 26, 2013 at 5:42 PM
Edited Jan 26, 2013 at 5:42 PM

TYVM for the feedback, but my situation is a little different :)

I got it working now, but good enough.

My form (generated by Ajax.BeginForm helper) is processed by a controller, which returns JSON data in a simple format:

{"Message":"Message text.","Valid":false}

Atm I assume for simplicity that valid:false can only mean invalid captcha. The controller with CaptchaVerify attribute is working good enough, deciding whether the captcha input was correct or not.

If i get false, i just invalidate the input with validator.showErrors({ "CaptchaInputText": "Wrong captcha value." }); fired on the onSuccess event.

But the captcha value, which is shown on the CaptchaImage doesn't seem to be correct any more. I guess it can be checked only once and needs to be dynamically regenerated after the first AJAX-query. Is there a way to achieve it with javascript? Furthermore, if i try to click on the Refresh button, i get this 500-error:

 

[ArgumentException: The key is to update incorrect.]
   CaptchaMvc.Infrastructure.DefaultCaptchaManager.Update(HttpRequestBase request) in g:\CodePlex\Captcha\CaptchaMvc(Mvc 3)\Infrastructure\DefaultCaptchaManager.cs:416
   CaptchaMvc.Controllers.DefaultCaptchaController.Refresh() in g:\CodePlex\Captcha\CaptchaMvc(Mvc 3)\Controllers\DefaultCaptchaController.cs:39
   lambda_method(Closure , ControllerBase , Object[] ) +102
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +59
 ..... etc


Any ideas on how to handle it? Btw, if you understand Russian, you can check it out here: outstaff-service.ru, the big blue button. Thanks in advance.
Coordinator
Jan 27, 2013 at 5:03 AM

Good question, you need to update the value in ImageElementId and TokenElementId fields. In the current version, you can only do so:

var h =
                new HtmlHelper(
                    new ViewContext(ControllerContext, new WebFormView(ControllerContext, "empty"), new ViewDataDictionary(),
                                    new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage());
            var buildInfo = CaptchaUtils.CaptchaManager.GenerateNew(h,
                                                    new ParameterModelContainer(new[]
                                                                                    {
                                                                                        new ParameterModel(
                                                                                            DefaultCaptchaManager.LengthAttribute, 5),
                                                                                    }));

            return
                Json(new Dictionary<string, string>
                         {
                             {buildInfo.ImageElementId, buildInfo.ImageUrl},
                             {buildInfo.TokenElementId, buildInfo.TokenValue}
                         });

 In the next version I'll make it easier.

Regards,
Vyacheslav Volkov

Jan 27, 2013 at 11:56 AM

Thanks, at last I got everything working.

On the client-side I'm using this code (using jQuery and MVC's Ajax.BeginForm built-in onSuccess event handling):

        $('#CaptchaImage').attr('src', data.Captcha.CaptchaImage);
        $('#CaptchaDeText').attr('value', data.Captcha.CaptchaDeText);

Note, that the dictionary in my case is packed into a complex object this way:

 

return Json(new { Message = "Wrong CAPTCHA value. Please try again.", Valid = false,
                    Captcha = new Dictionary<string, string>
                    {
                        {buildInfo.ImageElementId, buildInfo.ImageUrl},
                        {buildInfo.TokenElementId, buildInfo.TokenValue}
                    }  
                }); 

Vyacheslav, thank you again for the fast feedback and for the great module!

 

Jan 23, 2014 at 3:23 AM
Hi,

I just tried the above implementation mentioned by Recky and Vyacheslav.
For me, i dont have to referesh the captcha when it is valid. Please let me know how to accomplish it.

If i dont update the $('#CaptchaImage') and $('#CaptchaDeText') when the captcha is valid and try to refresh the captcha again i get an error "The key is to update incorrect".

Thanks
Feb 19, 2014 at 1:29 AM
Edited Feb 19, 2014 at 1:30 AM
Hi,

I have an Ajax post for partial view with MVC captcha and the model state returns the errors as JSON and I want to display the error for Captcha invalid.With the following code it is not displaying the error message for captcha invalid.
 return Json(new
                {
                    Status = "failed",
                    errors = ModelState.Values.SelectMany(x => x.Errors)

                });
partial view javascript if model is not valid:
for(var error in result.errors) {
                            $('#errormessagediv').append(error.ErrorMessage);

                        }
Sep 16, 2014 at 7:25 PM
vvs0205 wrote:
Good question, you need to update the value in ImageElementId and TokenElementId fields. In the current version, you can only do so: var h = new HtmlHelper( new ViewContext(ControllerContext, new WebFormView(ControllerContext, "empty"), new ViewDataDictionary(), new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage()); var buildInfo = CaptchaUtils.CaptchaManager.GenerateNew(h, new ParameterModelContainer(new[] { new ParameterModel( DefaultCaptchaManager.LengthAttribute, 5), })); return Json(new Dictionary<string, string> { {buildInfo.ImageElementId, buildInfo.ImageUrl}, {buildInfo.TokenElementId, buildInfo.TokenValue} });  In the next version I'll make it easier. Regards, Vyacheslav Volkov
How did you "make it easier"?
I have a similar requirement.

Thank you for you hard work on this solution.
Coordinator
Sep 17, 2014 at 5:24 PM
Hi there,
Now you can use this code to generate new value (see example MvcCaptcha.Examples.Mvc4\GettingStarted\Views\Home\AjaxForm2):
IUpdateInfoModel captchaValue = this.GenerateCaptchaValue(5);
return Json(new
                {
                    Message = "Captcha is not valid",
                    Captcha =
                new Dictionary<string, string>
                    {
                        {captchaValue.ImageElementId, captchaValue.ImageUrl},
                        {captchaValue.TokenElementId, captchaValue.TokenValue}
                    }
                });
Regards,
Vyacheslav Volkov