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.
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; }
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"/> }
Example is available here.
In the same way we can define methods for creating CaptchaUtils.BuilderProviderFactory, CaptchaUtils.ImageGeneratorFactory.