
In this second part, i will show how to make an ASP.NET Validation control that uses the reCaptcha framework.
I named this control ReCaptchaValidator. This validator extends the abstract class BaseValidator. And so soon we arrived to the first point.
ReCaptchaValidator could implement the interface IValidator, but he would lose a functionality that is very useful. the property ValidationGroup. This property allows you to tell the validator that he will only make the validation when an input with the same validation group makes a post back.
Example
<asp:Button ValidationGroup="test" runat="server"/>
When the above control makes a post back to the server, only the controls in the validation group named “test” will be called.
So, the ReCaptchaValidater extends BaseValidator. Starting explanation, all the action will occur in the EvaluateIsValid, a method inherited from BaseValidator:
protected override bool EvaluateIsValid() {
string parameters = ReCaptchaParameters();
string result = ValidateCaptcha(parameters);
bool isValid = result.Equals("true\nsuccess");
if( !isValid ) {
ErrorMessage = "Your are not a human!";
Controls.Add( new LiteralControl(ErrorMessage));
}
return isValid;
}
This method will call the private method ReCaptchaParameters that will return all the parameters needed to make the Web Request to the reCaptcha url. This Web Request will be made by the method ValidateCaptcha to which the parameters are passed. (ReCaptchaParameters and ValidateCaptcha are defined below)
Then the result of the Web Request is validated. If the result is different from the string “true\nsuccess”, then the user didn’t inserted the captcha correctly (or a BOT is trying to register in your site).
/// <summary>
/// Gathers the parameters for reCaptchaValidator
/// </summary>
/// <returns></returns>
private static string ReCaptchaParameters() {
string response = GetFormValue("recaptcha_response_field");
string challenge = GetFormValue("recaptcha_challenge_field");
string IP = HttpContext.Current.Request.UserHostName;
StringBuilder builder = new StringBuilder();
builder.AppendFormat( "privatekey={0}&", PrivateKey );
builder.AppendFormat( "remoteip={0}&", IP );
builder.AppendFormat( "challenge={0}&", challenge );
builder.AppendFormat( "response={0}", response );
return builder.ToString();
}
/// <summary>
/// Validates the capcha inserted by the user
/// </summary>
/// <param name="parameters">parameters to verify</param>
/// <returns>response of the capcth</returns>
private static string ValidateCaptcha( string parameters ) {
try {
WebRequest request =
WebRequest.Create( "http://api-verify.recaptcha.net/verify" );
request.ContentType = "application/x-www-form-urlenpred";
request.Method = "POST";
StreamWriter writer =
new StreamWriter( request.GetRequestStream() );
writer.Write( parameters );
writer.Close();
//Response
HttpWebResponse webResponse =
(HttpWebResponse)request.GetResponse();
StreamReader myReader =
new StreamReader( webResponse.GetResponseStream() );
string response = myReader.ReadToEnd();
myReader.Close();
webResponse.Close();
return response;
}catch( WebException ) {
//Do Some Exception threatment
return string.Empty;
}
}
In the Render method we will render all the code necessary to show the reCaptcha validator:
/// <summary>
/// Writes the javascript to show the validation Captcha
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer) {
writer.Write(
@"
<script>
var RecaptchaOptions = {{theme : 'blackglass', tabindex : 2}};
</script>
<script type='text/javascript'
src='http://api.recaptcha.net/challenge?k={0}'&
gt;</script>
<noscript>
<iframe src='http://api.recaptcha.net/noscript?k={0}'
height='300' width='500'
frameborder='0'></iframe><br/>
<textarea name='recaptcha_challenge_field' rows='3'
cols='40'></textarea>
<input type='hidden' name='recaptcha_response_field'
value='manual_challenge'>
</noscript>", PublicKey );
dummy.Visible = false;
base.Render(writer);
}
Noticed the dummy.Visible = false in the render? This is the second point i wanted to talk about. The validators that inherit from BaseValidator were made to validate other controls. In this case, we don’t want to validate any asp.net control. The control we want to validate is provided by a third party code (all the javascript in the render method).
Because of this fact, the validator has an hack. A TextBox with the name “dummy” is created and it’s ID is passed to the property of BaseValidator, ControlToValidate. This way we can surpass the limitation of BaseValidator. Notice that, if this validator didn’t extend BaseValidator, but implemented IValidator, this hack would not be needed.
I made this hack on the event OnInit:
protected override void OnInit(EventArgs e) {
dummy.ID = "aaa";
Controls.Add(dummy);
ControlToValidate = "aaa";
base.OnInit(e);
}
An that’s it. A free captcha control that you can use in all yout web applications.
For your commodity, the ReCaptchaValidator code is available here (in C#) and here (in VB) (Note: the VB code was created using a converter so it might not be exactly like the C# original file).
Hi,
Could you please help me out? i don’t understand C#. could you provide me the code in VB?
Any help would be appreiciated.
Thanks
Hello Vasant,
i added the VB code. I used a converter, so it might not be exactly as the original code.
I hope it helps.
Hi,
I am a bit confused as rather this is the CAPTCHA control or the validator that validates the CAPTCHA?
As I am trying to modify reCAPTCHA’s source code to add the ValidationGroup attribute.
Thanks,
Paul
Well, the answer is both
It’s a CAPTCHA control because it displays a CAPTCHA and, at the same time validates the CAPTCHA inserted by the user.
And if you use the code above, you don’t need to add the property ValidationGroup. The ReCaptchaValidator Control inherits it from BaseValidator.
Does this work when its in a CreateUserWizard form? I’m using the prebuilt control from recaptcha.googlegroups.com, but it doesn’t work properly in a CreateUserWizard. For some reason, in this case, two requests are sent to api-verify.recaptcha.net. If the first one is successful, the second one will fail (I think it’s because the challenge code is no longer valid).
Hello Mike,
I Tested with a very simple example and the answer is yes, it works.
If you set the ValidationGroup of the ReCaptchaValidator to the same as the validators in the CreateUserWizard control, then it should work fine.