About .NET, ASP.NET, MVC, C#, WPF, WCF and everything related to .NET and more.

Captcha Mvc [Use a factory to create captcha]

Often asked what to do if you want to have multiple instances of captcha with different behavior?
For do this you can use factories:

  • CaptchaUtils.CaptchaManagerFactory - used to create an ICaptchaManager.
  • CaptchaUtils.BuilderProviderFactory - used to create an ICaptchaBuilderProvider.
  • CaptchaUtils.ImageGeneratorFactory - used to create an IGenerateImage.
Consider an example in which we are use a multiple instance of captcha in the same view.
For this, we need that each instance of captcha have their id different from other. Will do so each instance of captcha have their instance of ICaptchaManager. Write this code in Global.asax.cs:
public const string MultipleParameterKey = "_multiple_";

private static readonly ConcurrentDictionary<int, ICaptchaManager> CaptchaManagers =
    new ConcurrentDictionary<int, ICaptchaManager>();

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    CaptchaUtils.CaptchaManagerFactory = GetCaptchaManager;
}

private static ICaptchaManager GetCaptchaManager(IParameterContainer parameterContainer)
{
    int numberOfCaptcha;
    if (parameterContainer.TryGet(MultipleParameterKey, out numberOfCaptcha))
        return CaptchaManagers
            .GetOrAdd(numberOfCaptcha, CreateCaptchaManagerByNumber);
    //If not found parameter return default manager.
    return CaptchaUtils.CaptchaManager;
}

private static ICaptchaManager CreateCaptchaManagerByNumber(int i)
{
    var captchaManager = new DefaultCaptchaManager(new SessionStorageProvider());
    captchaManager.ImageElementName += i;
    captchaManager.InputElementName += i;
    captchaManager.TokenElementName += i;
    captchaManager.ImageUrlFactory = (helper, pair) =>
                                            {
                                                var dictionary = new RouteValueDictionary();
                                                dictionary.Add(captchaManager.TokenParameterName, pair.Key);
                                                dictionary.Add(MultipleParameterKey, i);
                                                return helper.Action("Generate", "DefaultCaptcha", dictionary);
                                            };
    captchaManager.RefreshUrlFactory = (helper, pair) =>
                                            {
                                                var dictionary = new RouteValueDictionary();
                                                dictionary.Add(MultipleParameterKey, i);
                                                return helper.Action("Refresh", "DefaultCaptcha", dictionary);
                                            };
    return captchaManager;
}
We define method GetCaptchaManager for create instance of ICaptchaManager, it takes interface IParameterContainer that provide access to parameters. Parameters can be taken from request or passed in a method of create a captcha.
Next, we declare a constant MultipleParameterKey, that contains key of parameter that define the number of captcha in a view. In the GetCaptchaManager method, we check if IParameterContainer contains a key with name MultipleParameterKey so we need to provide specific ICaptchaManager for processing. This makes CaptchaManagers dictionary and CreateCaptchaManagerByNumber method.
The CreateCaptchaManagerByNumber method creates new instance of ICaptchaManager and assign a unique name for parameters ImageElementName, InputElementName, TokenElementName and sets factories to create URL for image and refresh. In it, he defines MultipleParameterKey additional parameter with a value equal to the current number.
Then we can define a multiple instance of captcha in the same view, example:
@using CaptchaMvc.HtmlHelpers
@using CaptchaMvc.Models
@using FactoryExample
@{
    ViewBag.Title = "Index";
}

<h2>The default captcha render with Html.Captcha(5, new ParameterModel(MvcApplication.MultipleParameterKey, 0))</h2>
@using (Html.BeginForm())
{
    //Adding a hidden field with a number of captcha
    @Html.Hidden(MvcApplication.MultipleParameterKey, 0)

    //Pass a number of captcha as parameter
    @Html.Captcha(5, new ParameterModel(MvcApplication.MultipleParameterKey, 0))
    <br />
    <input type="submit" value="Send"/>
}


<h2>The captcha render with Html.Captcha("Refresh", "Input", 5, 
new ParameterModel(MvcApplication.MultipleParameterKey, 0))</h2>
@using (Html.BeginForm())
{
    //Adding a hidden field with a number of captcha
    @Html.Hidden(MvcApplication.MultipleParameterKey, 1)

    //Pass a number of captcha as parameter
    @Html.Captcha("Refresh", "Input", 5, new ParameterModel(MvcApplication.MultipleParameterKey, 1))
    <br />
    <input type="submit" value="Send"/>
}

<h2>The captcha render with Html.Captcha("Refresh", "Input", 5, "Is required field.", true, 
new ParameterModel(MvcApplication.MultipleParameterKey, 0))</h2>
@using (Html.BeginForm())
{
    //Adding a hidden field with a number of captcha
    @Html.Hidden(MvcApplication.MultipleParameterKey, 2)

    //Pass a number of captcha as parameter
    @Html.Captcha("Refresh", "Input", 5, "Is required field.", true, new ParameterModel(MvcApplication.MultipleParameterKey, 2))
    <br />
    <input type="submit" value="Send"/>
}
Note that when we create a captcha we pass special parameter new ParameterModel(MvcApplication.MultipleParameterKey, number) and define hidden field @Html.Hidden(MvcApplication.MultipleParameterKey, number) it is necessary that our method can determine which instance ICaptchaManager use. After these steps, we can start the application and make sure everything is working.
Example is available here.
In the same way we can define methods for creating CaptchaUtils.BuilderProviderFactory, CaptchaUtils.ImageGeneratorFactory.

Comments
Leave a Reply
*bold*
_italics_
+underline+
* Bullet List
** Bullet List 2
# Number List
## Number List 2
{"Do not apply formatting"}
{code:language} code here {code:language}.
Supports: aspx c#, c#, c++, html, sql, xml
[url:http://www.example.com]