ASP.NET Core网站中为多处「联系我们」表单集成reCaptcha v2
给ASP.NET Core的「联系我们」分部视图添加reCaptcha v2完整方案
既然你已经有了统一的分部视图和视图模型,那给所有页面的表单加上reCaptcha v2就非常高效了,不用逐个页面修改。我给你梳理一套完整的实现步骤:
1. 准备reCaptcha密钥并配置到项目
首先去Google的reCaptcha控制台注册你的网站,获取Site Key(前端用)和Secret Key(后端验证用)。
把密钥存到appsettings.json里,避免硬编码:
"RecaptchaSettings": { "SiteKey": "你的Site Key", "SecretKey": "你的Secret Key" }
然后创建一个配置类来映射这些设置:
public class RecaptchaSettings { public string SiteKey { get; set; } public string SecretKey { get; set; } }
在Program.cs里注册这个配置:
builder.Services.Configure<RecaptchaSettings>(builder.Configuration.GetSection("RecaptchaSettings"));
2. 改造视图模型,添加reCaptcha绑定
给你的GetInTouchViewModel加一个属性,用来接收前端的reCaptcha验证token。因为Google的reCaptcha会自动生成一个名为g-recaptcha-response的隐藏字段,我们用BindProperty特性让模型自动绑定这个字段:
public class GetInTouchViewModel { [Required(ErrorMessage = "Please enter your name")] public string Name { get; set; } // 保留你原有的其他表单属性... [Required(ErrorMessage = "Please complete the reCAPTCHA verification")] [BindProperty(Name = "g-recaptcha-response")] public string RecaptchaToken { get; set; } }
3. 修改分部视图,添加reCaptcha控件
在你的_GetInTouchSection.cshtml里注入配置,然后添加reCaptcha的控件和脚本:
@model GetInTouchViewModel @inject IOptions<RecaptchaSettings> RecaptchaSettings <form asp-action="SubmitGetInTouch" method="post"> <!-- 保留你原有的表单字段,比如Name输入框等 --> <div class="form-group"> <label asp-for="Name"></label> <input asp-for="Name" class="form-control" /> <span asp-validation-for="Name" class="text-danger"></span> </div> <!-- 添加reCAPTCHA验证控件 --> <div class="form-group mt-3"> <div class="g-recaptcha" data-sitekey="@RecaptchaSettings.Value.SiteKey"></div> <span asp-validation-for="RecaptchaToken" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary mt-3">Submit</button> </form> <!-- 把脚本放到布局的Scripts分部,避免重复加载 --> @section Scripts { <script src="https://www.google.com/recaptcha/api.js" async defer></script> }
注意:如果你的布局视图里没有@RenderSection("Scripts", required: false),记得加上,一般放在</body>标签前,这样脚本只会加载一次。
4. 实现后端验证服务(关键!)
客户端验证可以被绕过,所以必须在后端验证token的有效性。先创建一个验证服务:
4.1 定义服务接口和实现
public interface IRecaptchaService { Task<bool> VerifyTokenAsync(string token, string remoteIp); } public class RecaptchaService : IRecaptchaService { private readonly HttpClient _httpClient; private readonly RecaptchaSettings _settings; public RecaptchaService(HttpClient httpClient, IOptions<RecaptchaSettings> settings) { _httpClient = httpClient; _settings = settings.Value; } public async Task<bool> VerifyTokenAsync(string token, string remoteIp) { var response = await _httpClient.PostAsync( "https://www.google.com/recaptcha/api/siteverify", new FormUrlEncodedContent(new Dictionary<string, string> { ["secret"] = _settings.SecretKey, ["response"] = token, ["remoteip"] = remoteIp })); if (!response.IsSuccessStatusCode) return false; var result = await response.Content.ReadFromJsonAsync<RecaptchaVerificationResult>(); return result.Success; } } // 用来解析Google返回的验证结果 public class RecaptchaVerificationResult { public bool Success { get; set; } public List<string> ErrorCodes { get; set; } }
4.2 注册服务到DI容器
在Program.cs里添加:
builder.Services.AddHttpClient<IRecaptchaService, RecaptchaService>();
5. 在控制器提交方法里验证reCaptcha
修改处理表单提交的Action,注入IRecaptchaService并验证token:
public class YourController : Controller { private readonly IRecaptchaService _recaptchaService; public YourController(IRecaptchaService recaptchaService) { _recaptchaService = recaptchaService; } [HttpPost] public async Task<IActionResult> SubmitGetInTouch(GetInTouchViewModel model) { // 获取用户的IP地址,用于reCaptcha验证 var remoteIp = HttpContext.Connection.RemoteIpAddress?.ToString(); // 验证reCaptcha token var isRecaptchaValid = await _recaptchaService.VerifyTokenAsync(model.RecaptchaToken, remoteIp); if (!isRecaptchaValid) { ModelState.AddModelError(nameof(model.RecaptchaToken), "reCAPTCHA verification failed. Please try again."); } // 检查整个模型的有效性 if (!ModelState.IsValid) { // 返回分部视图,保持页面上下文 return PartialView("_GetInTouchSection", model); } // 这里处理你的表单提交逻辑,比如发送邮件、保存到数据库等 // ... // 返回成功页面或提示 return RedirectToAction("ContactSuccess"); } }
6. 测试和优化
- 测试场景:不勾选reCaptcha直接提交,应该会触发客户端提示;勾选后提交,后端会验证token有效性。
- 错误消息优化:如果需要更具体的错误(比如超时、无效密钥),可以根据
RecaptchaVerificationResult里的ErrorCodes来返回对应消息,但一般通用错误就够了。 - 样式调整:可以给reCaptcha控件加一些CSS类,让它和你的页面风格统一。
内容的提问来源于stack exchange,提问作者Neil
相关产品推荐
相关产品推荐

