RecaptchaMvc
After searching for a solution to use reCAPTCHA with ASP.NET MVC, I have come up with no way that works well with the MVC architecture. Armed with frustration and my OCD tendencies, I created RecaptchaMvc.
To get started you need a free public and private key from reCAPTCHA and the RecaptchaMvc Beta 1.
RecaptchaMvc comes with an HTML helper to render the appropriate markup for the reCAPTCHA service. The HTML helper requires an IRecaptchaModelState object that contains the public key and error code (more on this later). My suggestion would be to create a ViewPage with the generic typed Model like so.
1: using System.Web.Mvc;
2: using RecaptchaMvc;
3:
4: public class RecaptchaTest : viewPage<IRecaptchaModelState> {}
Now you can import the RecaptchaMvc framework to the page and use the HTML helper to render the appropriate markup.
1: <%@ Import Namespace="RecaptchaMvc" %>
2:
3: <%= Html.Recaptcha(ViewData.Model) % >
The HTML helper has three overloads. One accepts only the IRecaptchaModelState object. The other two accept either a dictionary or object that describe the options to be rendered for the reCAPTCHA service. The reCAPTCHA client API page shows the options that reCAPTCHA can use.
In your controller you would return the View with the appropriate IRecaptchaModelState. The framework comes with a RecaptchaModelState object that allows you to construct it with your public key and error code (more on this later).
1: using System.Web.Mvc;
2: using RecaptchaMvc;
3:
4: [AcceptVerbs(HttpVerbs.Get)]
5: public ActionResult RecaptchaTest() {
6: string publicKey = WebConfigurationManager.AppSettings["RecaptchaPublicKey"]
7: return View(new RecaptchaModelState(publicKey));
8: }
I store my public and private keys from reCAPTCHA in the web.config file, so I can easily change them depending if it is deployed in development or production. I use the AcceptVerbsAttribute to limit which requests use this action method. When the View renders it will have the appropriate IRecaptchaModelState in its ViewData.Model property for the HTML helper to use.
When the form is submitted to the server, we need to validate the response with reCAPTCHA. The framework includes two methods for just this task: TryValidateRecaptcha and ValidateRecaptcha. Both methods have three overloads. One that asks for the private key. This overload will use the DefaultValueProvider to retrieve the challenge and the response from the post. One asks for the private key and an IValueProvider to retrieve the challenge and the response from the provider. The last one accepts an IRecaptchaRequest object that specifies the private key, the client's remote IP, the challenge and the response. TryValidateRecaptcha will return an IRecaptchaResponse object that specifies whether the response was valid and an error code if validation failed. The ValidateRecaptcha method will throw a RecaptchaValidationException if validation fails. The RecaptchaValidationException contains an IRecaptchaResponse object. You can use both of these methods to validate the reCAPTCHA response and display the appropriate view to the user. If validation fails, you can return a view with an IRecaptchaModelState object that contains the public key and the error code from the IRecaptchaResponse.
1: using System.Web.Mvc;
2: using RecaptchaMvc;
3:
4: [AcceptVerbs(HttpVerbs.Post)]
5: public ActionResult RecaptchaTest(FormCollection form) {
6: string privateKey = WebConfigurationManager.AppSettings["RecaptchaPrivateKey"];
7: IRecaptchaResponse response = TryValidateRecaptcha(privateKey, form);
8: if(!response.IsValid) {
9: string publicKey = WebConfigrationManager.AppSettings["RecaptchaPublicKey"];
10: return View(new RecaptchaModelState(publicKey, response.ErrorCode));
11: }
12: return RedirectToAction("RecaptchaTestPassed");
13: }
1: using System.Web.Mvc;
2: using RecaptchaMvc;
3:
4: [AcceptVerbs(HttpVerbs.Post)]
5: public ActionResult RecaptchaTest(FormCollection form) {
6: try {
7: string privateKey = WebConfigurationManager.AppSettings["RecaptchaPrivateKey"];
8: ValidateRecaptcha(privateKey, form);
9: }
10: catch(RecaptchaValidationException ex) {
11: string publicKey = WebConfigrationManager.AppSettings["RecaptchaPublicKey"];
12: return View(new RecaptchaModelState(publicKey, ex.Response.ErrorCode));
13: }
14: return RedirectToAction("RecaptchaTestPassed");
15: }
Well there you have it. A solution to using reCAPTCHA in ASP.NET MVC without any tricky hacks. If you have any suggestions for improvement or find a bug, please let me know. I invite all constructive feedback, positive or negative.