Blazor recaptcha 验证属性 IHttpContextAccessor 始终为 null

2023-12-27

我想尝试一下使用 Blazor 服务器端,到目前为止,我已经设法以某种方式克服了大多数令人头疼的问题,并且很享受它,直到现在。

我正在尝试为 Google Recaptcha v3 编写一个验证器,它需要用户的 IP 地址。通常我会通过以下方式获取 IHttpContextAccessor:

var httpContextAccessor = (IHttpContextAccessor)validationContext.GetService(typeof(IHttpContextAccessor));

但现在返回 null!我还发现尝试以相同的方式获取 IConfiguration 失败了,但为此,我可以在 Startup.cs 中创建一个静态属性。

这是一天工作中的最后一个障碍,这让我感到困惑。

关于如何将该 IP 地址输入验证器有什么想法吗?

Thanks!

Edit:

我刚刚发现 httpContextAccessor 为空的错误!

((System.RuntimeType)validationContext.ObjectType).DeclaringMethod抛出“System.InvalidOperationException”类型的异常

这是验证器:

public class GoogleReCaptchaValidationAttribute : ValidationAttribute
    {

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        Lazy<ValidationResult> errorResult = new Lazy<ValidationResult>(() => new ValidationResult("Google reCAPTCHA validation failed", new String[] { validationContext.MemberName }));

        if (value == null || String.IsNullOrWhiteSpace(value.ToString()))
        {
            return errorResult.Value;
        }

        var configuration = Startup.Configuration;
        string reCaptchResponse = value.ToString();
        string reCaptchaSecret = configuration["GoogleReCaptcha:SecretKey"];
        IHttpContextAccessor httpContextAccessor = validationContext.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor;
        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("secret", reCaptchaSecret),
            new KeyValuePair<string, string>("response", reCaptchResponse),
            new KeyValuePair<string, string>("remoteip", httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString())
        });

        HttpClient httpClient = new HttpClient();
        var httpResponse = httpClient.PostAsync("https://www.google.com/recaptcha/api/siteverify", content).Result;
        if (httpResponse.StatusCode != HttpStatusCode.OK)
        {
            return errorResult.Value;
        }

        String jsonResponse = httpResponse.Content.ReadAsStringAsync().Result;
        dynamic jsonData = JObject.Parse(jsonResponse);
        if (jsonData.success != true.ToString().ToLower())
        {
            return errorResult.Value;
        }

        return ValidationResult.Success;

    }
}

对于这个问题,是由于当数据注释验证器 https://github.com/aspnet/AspNetCore/blob/057ec9cfae1831f91f1a0e01397059b20431db7b/src/Components/Forms/src/DataAnnotationsValidator.cs call 添加数据注释验证 https://github.com/aspnet/AspNetCore/blob/057ec9cfae1831f91f1a0e01397059b20431db7b/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs,它没有将 IServiceProvider 传递给ValidationContext.

对于这个问题,你可以检查一下使依赖解析可用于 EditContext 表单验证,以便自定义验证器可以访问服务。第11397章 https://github.com/aspnet/AspNetCore/issues/11397

private static void ValidateModel(EditContext editContext, ValidationMessageStore messages)
{
    var validationContext = new ValidationContext(editContext.Model);
    var validationResults = new List<ValidationResult>();
    Validator.TryValidateObject(editContext.Model, validationContext, validationResults, true);

    // Transfer results to the ValidationMessageStore
    messages.Clear();
    foreach (var validationResult in validationResults)
    {
        foreach (var memberName in validationResult.MemberNames)
        {
            messages.Add(editContext.Field(memberName), validationResult.ErrorMessage);
        }
    }

    editContext.NotifyValidationStateChanged();
}

对于解决方法,您可以实施自己的DataAnnotationsValidator and AddDataAnnotationsValidation .

请按照以下步骤操作:

  1. Custom DataAnnotationsValidator

    public class DIDataAnnotationsValidator: DataAnnotationsValidator
    {
        [CascadingParameter] EditContext DICurrentEditContext { get; set; }
    
        [Inject]
        protected IServiceProvider ServiceProvider { get; set; }
        protected override void OnInitialized()
        {
            if (DICurrentEditContext == null)
            {
                throw new InvalidOperationException($"{nameof(DataAnnotationsValidator)} requires a cascading " +
                    $"parameter of type {nameof(EditContext)}. For example, you can use {nameof(DataAnnotationsValidator)} " +
                    $"inside an EditForm.");
            }
    
            DICurrentEditContext.AddDataAnnotationsValidationWithDI(ServiceProvider);
        }
    }
    
  2. Custom EditContextDataAnnotationsExtensions

    public static class EditContextDataAnnotationsExtensions
    {
        private static ConcurrentDictionary<(Type ModelType, string FieldName), PropertyInfo> _propertyInfoCache
        = new ConcurrentDictionary<(Type, string), PropertyInfo>();
    
        public static EditContext AddDataAnnotationsValidationWithDI(this EditContext editContext, IServiceProvider serviceProvider)
        {
            if (editContext == null)
            {
                throw new ArgumentNullException(nameof(editContext));
            }
    
            var messages = new ValidationMessageStore(editContext);
    
            // Perform object-level validation on request
            editContext.OnValidationRequested +=
                (sender, eventArgs) => ValidateModel((EditContext)sender, serviceProvider, messages);
    
            // Perform per-field validation on each field edit
            editContext.OnFieldChanged +=
                (sender, eventArgs) => ValidateField(editContext, serviceProvider, messages, eventArgs.FieldIdentifier);
    
            return editContext;
        }
        private static void ValidateModel(EditContext editContext, IServiceProvider serviceProvider,ValidationMessageStore messages)
        {
            var validationContext = new ValidationContext(editContext.Model, serviceProvider, null);
            var validationResults = new List<ValidationResult>();
            Validator.TryValidateObject(editContext.Model, validationContext, validationResults, true);
    
            // Transfer results to the ValidationMessageStore
            messages.Clear();
            foreach (var validationResult in validationResults)
            {
                foreach (var memberName in validationResult.MemberNames)
                {
                    messages.Add(editContext.Field(memberName), validationResult.ErrorMessage);
                }
            }
    
            editContext.NotifyValidationStateChanged();
        }
    
        private static void ValidateField(EditContext editContext, IServiceProvider serviceProvider, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier)
        {
            if (TryGetValidatableProperty(fieldIdentifier, out var propertyInfo))
            {
                var propertyValue = propertyInfo.GetValue(fieldIdentifier.Model);
                var validationContext = new ValidationContext(fieldIdentifier.Model, serviceProvider, null)
                {
                    MemberName = propertyInfo.Name
                };
                var results = new List<ValidationResult>();
    
                Validator.TryValidateProperty(propertyValue, validationContext, results);
                messages.Clear(fieldIdentifier);
                messages.Add(fieldIdentifier, results.Select(result => result.ErrorMessage));
    
                // We have to notify even if there were no messages before and are still no messages now,
                // because the "state" that changed might be the completion of some async validation task
                editContext.NotifyValidationStateChanged();
            }
        }
    
        private static bool TryGetValidatableProperty(in FieldIdentifier fieldIdentifier, out PropertyInfo propertyInfo)
        {
            var cacheKey = (ModelType: fieldIdentifier.Model.GetType(), fieldIdentifier.FieldName);
            if (!_propertyInfoCache.TryGetValue(cacheKey, out propertyInfo))
            {
                // DataAnnotations only validates public properties, so that's all we'll look for
                // If we can't find it, cache 'null' so we don't have to try again next time
                propertyInfo = cacheKey.ModelType.GetProperty(cacheKey.FieldName);
    
                // No need to lock, because it doesn't matter if we write the same value twice
                _propertyInfoCache[cacheKey] = propertyInfo;
            }
    
            return propertyInfo != null;
        }
    
    }
    
  3. Replace DataAnnotationsValidator with DIDataAnnotationsValidator

    <EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
        @*<DataAnnotationsValidator />*@
        <DIDataAnnotationsValidator />
        <ValidationSummary />    
    </EditForm>
    
  4. For IHttpContextAccessor,您需要注册Startup.cs like

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
    
        services.AddHttpContextAccessor();
    }
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Blazor recaptcha 验证属性 IHttpContextAccessor 始终为 null 的相关文章

随机推荐

  • 有没有一种简单的方法可以使用名称标签连接到 EC2 实例?

    我们的团队正在 AWS 上工作 我们有很多实例 并且不断添加和删除它们 每个实例都有一个逻辑名称 这有助于我们了解它的用途以及找到它 然而 当我们想要连接到一个时 我们要么需要更新 ssh config始终保存文件 或者转到 Web 控制台
  • 无论如何,在 iPad 上加载页面后删除所有带有 javascript 的 标签?

    我知道我可以在使用 UIWebView 这就是我正在使用的 的 iPad 上加载页面后运行一行 javascript 代码 但我不知道我可以输入什么来浏览并删除所有标签 我还希望能够仅对页面的某些部分执行此操作 例如只删除特定标签内的标签
  • 使用控件拖动和单击对象

    现在我的场景有两个球体和一个位于 0 0 0 的点光源 使用控件 球体围绕该点旋转 但当我尝试拖动它们时 我无法让球体移动 有人可以快速看一下我的代码吗 谢谢 编辑 是的 它们会移动 但我需要它们可以独立于 THREE Controls 进
  • ESB 中有效负载的验证

    我有一个 HTTP POST REQUEST 它发送如下有效负载 键1 值1 键2 值2 键3 值3 我能够验证所有值 但每次都必须使用一个组件 在本例中 我使用了验证器 非空字符串 3 次 1 Is there any way that
  • 未找到架构 x86_64 os x lion 的符号

    当尝试使用 opencv 2 3 1 作为第三个来编译简单的 c test cpp 代码时 库 我收到以下错误消息 体系结构 x86 64 的未定义符号 cvLoadImage referenced from 这看起来像您没有正确链接到库
  • 尽管存在导入包,但 org.osgi.framework.BundleActivator 仍出现 ClassNotFoundException

    我尝试在较长一段时间后再次使用激活器运行一个非常简单的 OSGi Hello World 风格的示例 并得到 org osgi framework BundleActivator 的 ClassNotFoundException 请参阅下面
  • 逗号分隔的数组项列表

    VB NET 中是否有内置函数可以接受字符串数组并输出以逗号分隔的项目字符串 例子 function Sam Jane Bobby gt Sam Jane Bobby String Join YourArray 此外 如果您想从复选框列表
  • 参考模型编写两次迁移

    我有一个消息模型 Message 该模型作为 userTo 和 userFrom 因此有两个对 User 的引用 我该如何编写迁移 我的用户模型是User 谢谢 这是这个问题的完整答案 以防访问这个问题的人很难将所有内容放在一起 就像我第一
  • Java 静态字段初始化

    我刚刚花了半个小时弄清楚这件事 我已经设法修复了我的代码 但我不完全理解发生了什么 想知道是否有人可以阐明它 我有一个utils类型类 包含一些静态字段 例如数据库连接端点 其他程序根据手头的任务使用这些静态字段 本质上是一个图书馆 这是它
  • socket.io 私信

    我一直在网上搜索但没有运气 我正在尝试弄清楚如何将私人消息从一个用户发送到另一个用户 有很多片段 但我不确定客户端 服务器交互 如果我有要发送到的套接字的 ID 如何将其发送到服务器 以及如何确保服务器仅将消息发送到该接收者套接字 有没有人
  • ServletContextListener 不在部署时执行

    我正在尝试在部署我的战争文件时初始化流对象 我编写了一个实现 ServletContextListener 的初始化程序类 并将侦听器类标记添加到我的 web xml 中 问题是 当我向应用程序发出第一个请求时 而不是在部署应用程序时 会发
  • android:安装错误:未知失败 - 运行 apk 文件时

    昨天我在使用模拟器时没有遇到任何错误 但今天我遇到了这个错误 请检查下图 我已经运行和调试了很多次 但每次都没有得到相同的错误 安装apk文件后出现错误 请给出正确的答案 以便我可以正确修复模拟器并进行处理 现在 当我编译时 出现新错误 请
  • 用于选择性剥离 HTML 的正则表达式

    我正在尝试使用 PHP 解析一些 HTML 作为练习 将其仅作为文本输出 但我遇到了障碍 我想删除隐藏的所有标签style display none 请记住 标签可能包含其他属性和样式属性 到目前为止我的代码是这样的 page preg r
  • Windows 过滤平台 - 如何根据本地端口阻止传入连接?

    我正在尝试使用 WFP 设置一些过滤器来阻止到本地服务器的入站连接 例如 侦听端口 8080 的网络服务器 我有一个可以基于远程端口进行阻止的过滤器 因此我可以阻止我的计算机上的进程建立与端口 8080 的任何连接 但我不知道如何基于本地端
  • 您的捆绑包已锁定为 mimemagic (0.3.5),但在您的 Gemfile 中列出的任何源中都找不到该版本 [重复]

    这个问题在这里已经有答案了 今天我尝试为我的 Rails 6 1 0 构建一个带有主动存储的 docker 我收到以下错误 Your bundle is locked to mimemagic 0 3 5 but that version
  • 将 git 存储库上移一级

    Git初学者问题 我有一个小型私人网络项目 使用 msysgit 在本地进行版本控制 没有外部存储库 因为它只适合我 所以我基本上可以做任何我想做的事情 我已将其设置在项目目录中 即 webroot 中 现在必须创建第二个目录 与 webr
  • 在 Powershell 中访问音乐文件元数据[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 因此 多年来 从一台电脑 硬盘复制到另一台电脑 硬盘之间 我的音乐收藏有点混乱 所以我想以编程方式浏览每一个并更新下面屏幕截图中的文件元数据
  • 如何强制 Google Docs 从 Chrome 扩展中渲染 HTML 而不是 Canvas?

    Google 文档更新为基于画布的渲染而不是 HTML 渲染后 是否可以强制 Google 文档从 chrome 扩展而不是画布渲染 HTML 不知何故 像 Grammarly 这样的 chrome 扩展可以做到这一点 但我不完全确定如何做
  • Objective-C 和 Android [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我刚刚完成了一个相对较大的 Android 项目 它在我嘴里留下了一种苦涩的味道 因为我知道它永远不会在太阳系这一边最普遍的手机之一上运行 那个
  • Blazor recaptcha 验证属性 IHttpContextAccessor 始终为 null

    我想尝试一下使用 Blazor 服务器端 到目前为止 我已经设法以某种方式克服了大多数令人头疼的问题 并且很享受它 直到现在 我正在尝试为 Google Recaptcha v3 编写一个验证器 它需要用户的 IP 地址 通常我会通过以下方