C# 自定义模型绑定器未从值提供程序检索值

C# 自定义模型绑定器未从值提供程序检索值,c#,asp.net-core,asp.net-core-mvc,asp.net-core-1.1,C#,Asp.net Core,Asp.net Core Mvc,Asp.net Core 1.1,我有一个自定义模型绑定器,可以将发布的值转换为另一个模型。 问题是bindingContext.ValueProvider.GetValue(modelName)即使有从客户端发布的值也不返回任何值 行动方法 [HttpPost] public ActionResult Update([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")] Anothe

我有一个自定义模型绑定器,可以将发布的值转换为另一个模型。 问题是
bindingContext.ValueProvider.GetValue(modelName)
即使有从客户端发布的值也不返回任何值

行动方法

[HttpPost]
public ActionResult Update([DataSourceRequest] DataSourceRequest request, 
                           [Bind(Prefix = "models")] AnotherModel items)
{
    return Ok();
}
目标车型类别

[ModelBinder(BinderType = typeof(MyModelBinder))]
public class AnotherModel
{
    IEnumerable<Dictionary<string, object>> Items { get; set; }
}
public class RequestHeader : IHeader
{
    [Required]
    public PlatformType Platform { get; set; } //Windows / Android / Linux / MacOS / iOS

    [Required]
    public ApplicationType ApplicationType { get; set; } 

    [Required(AllowEmptyStrings = false)]
    public string UserAgent { get; set; } = null!; 

    [Required(AllowEmptyStrings = false)]
    public string ClientName { get; set; } = null!; 

    [Required(AllowEmptyStrings = false)]
    public string ApplicationName { get; set; } = null!;

    [Required(AllowEmptyStrings = true)]
    public string Token { get; set; } = null!; 


    public string ToSerializedString()
    {
        return JsonConvert.SerializeObject(this);
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelBinderProviders.Insert(0,new ParametersModelBinderProvider(configuration));
    });
}
快速观察显示ValueProvider确实有值

更新1

在updateaction方法中,当我可以遍历IFormCollection时,Request.Form拥有所有的键和值对。不确定为什么模型绑定器无法检索它

foreach (var f in HttpContext.Request.Form)
{
    var key = f.Key;
    var v = f.Value;
}

如果检查MVC的源代码,您会注意到“name[index]”形式的值将返回ValueProviderResult.None,需要单独处理

看起来你在试图解决错误的问题。我建议绑定到标准的集合类,如
Dictionary

要么

公共操作结果更新([DataSourceRequest]DataSourceRequest请求,
[Bind(Prefix=“models”)]字典项

public类AnotherModel:Dictionary{}
如果您不知道每个字典值在编译时的类型,那么定制活页夹就可以派上用场了

我的例子

在我的客户机中,我在请求中发送了一个头,这个头是Base64String(Json序列化对象)

对象->Json->Base64

标题不能是多行的。使用base64,我们得到一行

所有这些都适用于Body和其他来源

标题类

[ModelBinder(BinderType = typeof(MyModelBinder))]
public class AnotherModel
{
    IEnumerable<Dictionary<string, object>> Items { get; set; }
}
public class RequestHeader : IHeader
{
    [Required]
    public PlatformType Platform { get; set; } //Windows / Android / Linux / MacOS / iOS

    [Required]
    public ApplicationType ApplicationType { get; set; } 

    [Required(AllowEmptyStrings = false)]
    public string UserAgent { get; set; } = null!; 

    [Required(AllowEmptyStrings = false)]
    public string ClientName { get; set; } = null!; 

    [Required(AllowEmptyStrings = false)]
    public string ApplicationName { get; set; } = null!;

    [Required(AllowEmptyStrings = true)]
    public string Token { get; set; } = null!; 


    public string ToSerializedString()
    {
        return JsonConvert.SerializeObject(this);
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelBinderProviders.Insert(0,new ParametersModelBinderProvider(configuration));
    });
}
IHeader界面

public interface IHeader
{
}
型号活页夹

public class HeaderParameterModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        StringValues headerValue = bindingContext.HttpContext.Request.Headers.Where(h =>
        {
            string guid = Guid.NewGuid().ToString();
            return h.Key.Equals(bindingContext.ModelName ?? guid) | 
                   h.Key.Equals(bindingContext.ModelType.Name ?? guid) | 
                   h.Key.Equals(bindingContext.ModelMetadata.ParameterName); 
        }).Select(h => h.Value).FirstOrDefault();
        if (headerValue.Any())
        {
            try
            {
                //Convert started
                bindingContext.Model = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(headerValue)), bindingContext.ModelType);
                bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
            }
            catch
            {
            }
        }
        return Task.CompletedTask;
    }
}
型号活页夹供应商

public class HeaderParameterModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        StringValues headerValue = bindingContext.HttpContext.Request.Headers.Where(h =>
        {
            string guid = Guid.NewGuid().ToString();
            return h.Key.Equals(bindingContext.ModelName ?? guid) | 
                   h.Key.Equals(bindingContext.ModelType.Name ?? guid) | 
                   h.Key.Equals(bindingContext.ModelMetadata.ParameterName); 
        }).Select(h => h.Value).FirstOrDefault();
        if (headerValue.Any())
        {
            try
            {
                //Convert started
                bindingContext.Model = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(headerValue)), bindingContext.ModelType);
                bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
            }
            catch
            {
            }
        }
        return Task.CompletedTask;
    }
}
我们可以使用任何BindingSource

  • 身体
  • BindingSource自定义
  • BindingSource表单
  • BindingSourceFormFile
  • BindingSource标头
  • BindingSourceModelBinding
  • 绑定源路径
  • BindingSource查询
  • BindingSource服务
  • BindingSource专用

public class ParametersModelBinderProvider : IModelBinderProvider
{
    private readonly IConfiguration configuration;

    public ParametersModelBinderProvider(IConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType.GetInterfaces().Where(value => value.Name.Equals(nameof(ISecurityParameter))).Any() && BindingSource.Header.Equals(context.Metadata.BindingSource))
        {
            return new SecurityParameterModelBinder(configuration);
        }

        if (context.Metadata.ModelType.GetInterfaces().Where(value=>value.Name.Equals(nameof(IHeader))).Any() && BindingSource.Header.Equals(context.Metadata.BindingSource))
        {
            return new HeaderParameterModelBinder();
        }
        return null!;
    }
}
在Startup.cs中

[ModelBinder(BinderType = typeof(MyModelBinder))]
public class AnotherModel
{
    IEnumerable<Dictionary<string, object>> Items { get; set; }
}
public class RequestHeader : IHeader
{
    [Required]
    public PlatformType Platform { get; set; } //Windows / Android / Linux / MacOS / iOS

    [Required]
    public ApplicationType ApplicationType { get; set; } 

    [Required(AllowEmptyStrings = false)]
    public string UserAgent { get; set; } = null!; 

    [Required(AllowEmptyStrings = false)]
    public string ClientName { get; set; } = null!; 

    [Required(AllowEmptyStrings = false)]
    public string ApplicationName { get; set; } = null!;

    [Required(AllowEmptyStrings = true)]
    public string Token { get; set; } = null!; 


    public string ToSerializedString()
    {
        return JsonConvert.SerializeObject(this);
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelBinderProviders.Insert(0,new ParametersModelBinderProvider(configuration));
    });
}
控制器动作

ExchangeResult是我的结果类

[HttpGet(nameof(Exchange))]
public ActionResult<ExchangeResult> Exchange([FromHeader(Name = nameof(RequestHeader))] RequestHeader header)
{
    //RequestHeader previously was processed in modelbinder.
    //RequestHeader is null or object instance.  
    //Some instructions
}
[HttpGet(nameof(Exchange))]
公共操作结果交换([FromHeader(Name=nameof(RequestHeader))]RequestHeader头)
{
//RequestHeader以前是在modelbinder中处理的。
//RequestHeader为null或对象实例。
//一些说明
}

来自mvc集合活页夹的提示;您可以共享您的post数据吗?另外,您会收到
AnotherModel
而不是
List
,但是您会像
models[0]那样传递数据。属性
。您需要传递
models.property1
models.property2
…你的
数据源任务是什么
?如果你删除它会影响你的结果吗?@Rena这是剑道网格的帖子。这是从fiddler
sort=&group=&filter=&models%5B0%5D.Id=2&models%5B0%5D.FirstName=foo&models%5B0%5D.LastName=bar中捕获的帖子。我试过字典,但没用。请注意,
items
参数是网格行的集合。因此,它必须是
IEnumerable
,其中每个
字典
代表一行,所以您想绑定
列表
?您正在发布
模型[0]。Id=
,因此带有
T.Id
字段的
列表也可以工作。网格的列是动态的。我在设计时不知道“T”