Blazor reCAPTCHA验证属性IHttpConextAccessor始终为空 [英] Blazor recaptcha validation attribute IHttpContextAccessor is always null

查看:15
本文介绍了Blazor reCAPTCHA验证属性IHttpConextAccessor始终为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我以为我会尝试使用Blazor服务器端,到目前为止,我已经设法克服了大多数令人头疼的问题,并享受到了这一点。

我正在尝试为Google Recaptcha v3编写一个验证器,它需要用户的IP地址。通常,我只会使用以下命令获取IHttpConextAccessor:

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

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

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

关于如何将该IP地址放入验证器有什么建议吗?

谢谢!

编辑:

我刚发现将HTTPConextAccessor设为空的错误!

((System.RuntimeType)validationContext.ObjectType).DeclaringMethodthrew‘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;

    }
}

IServiceProvider

对于这个问题,是因为DataAnnotationsValidator 调用AddDataAnnotationsValidation时,没有将推荐答案传递给ValidationContext

对于此问题,您可以选中Make dependency resolution available for EditContext form validation so that custom validators can access services. #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();
}

作为解决办法,您可以实现您自己的DataAnnotationsValidatorAddDataAnnotationsValidation

按照以下步骤操作:

  1. 自定义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. 自定义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. DataAnnotationsValidator替换为DIDataAnnotationsValidator

    <EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
        @*<DataAnnotationsValidator />*@
        <DIDataAnnotationsValidator />
        <ValidationSummary />    
    </EditForm>
    
  4. IHttpContextAccessor您需要在Startup.cs点赞中注册

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
    
        services.AddHttpContextAccessor();
    }
    

这篇关于Blazor reCAPTCHA验证属性IHttpConextAccessor始终为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆