gpt4 book ai didi

asp.net-core - Blazor recaptcha 验证属性 IHttpContextAccessor 始终为 null

转载 作者:行者123 更新时间:2023-12-03 02:42:28 26 4
gpt4 key购买 nike

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

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

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

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

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

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

谢谢!

编辑:

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

((System.RuntimeType)validationContext.ObjectType).DeclaringMethodthrew an exception of type '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;

}
}

最佳答案

这个问题是由于DataAnnotationsValidator 时引起的调用AddDataAnnotationsValidation ,它没有将 IServiceProvider 传递给 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();
    }

关于asp.net-core - Blazor recaptcha 验证属性 IHttpContextAccessor 始终为 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57861930/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com