更改参数名称Web Api模型绑定

本文关键字:Api 模型 绑定 Web 参数 | 更新日期: 2023-09-27 18:28:00

我使用Web API模型绑定来解析URL中的查询参数。例如,这里有一个模型类:

public class QueryParameters
{
    [Required]
    public string Cap { get; set; }
    [Required]
    public string Id { get; set; }
}

当我调用类似/api/values/5?cap=somecap&id=1的东西时,这很好。

有没有什么方法可以更改模型类中属性的名称,但保持查询参数名称不变——例如:

public class QueryParameters
{
    [Required]
    public string Capability { get; set; }
    [Required]
    public string Id { get; set; }
}

我原以为将[Display(Name="cap")]添加到Capability属性中会起作用,但事实并非如此。是否有我应该使用的某种类型的数据注释?

控制器将有一个看起来像这样的方法:

public IHttpActionResult GetValue([FromUri]QueryParameters param)    
{
    // Do Something with param.Cap and param.id
}

更改参数名称Web Api模型绑定

您可以使用FromUri绑定属性的Name属性来使用与方法参数名称不同的查询字符串参数。

如果您传递的是简单的参数而不是QueryParameters类型,则可以像这样绑定值:

/api/values/5?cap=somecap&id=1
public IHttpActionResult GetValue([FromUri(Name = "cap")] string capabilities, int id)    
{
}

Web API使用与ASP.NET MVC稍有不同的模型绑定机制。它为主体中传递的数据使用格式化程序,为查询字符串中传递的信息使用模型绑定器(在您的情况下)。格式化程序尊重附加的元数据属性,而模型绑定器则不尊重。

因此,如果您在消息体而不是查询字符串中传递模型,您可以如下注释数据,它就会起作用:

public class QueryParameters
{
    [DataMember(Name="Cap")]
    public string Capability { get; set; }
    public string Id { get; set; }
}

你可能已经知道了。为了让它与查询字符串参数以及模型绑定器一起工作,您必须使用自己的自定义模型绑定器,该绑定器将实际检查和使用DataMember属性。

下面的一段代码可以做到这一点(尽管它与生产质量相去甚远):

public class MemberModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var model = Activator.CreateInstance(bindingContext.ModelType);
        foreach (var prop in bindingContext.PropertyMetadata)
        {
            // Retrieving attribute
            var attr = bindingContext.ModelType.GetProperty(prop.Key)
                                     .GetCustomAttributes(false)
                                     .OfType<DataMemberAttribute>()
                                     .FirstOrDefault();
            // Overwriting name if attribute present
            var qsParam = (attr != null) ? attr.Name : prop.Key;
            // Setting value of model property based on query string value
            var value = bindingContext.ValueProvider.GetValue(qsParam).AttemptedValue;
            var property = bindingContext.ModelType.GetProperty(prop.Key);
            property.SetValue(model, value);
        }
        bindingContext.Model = model;
        return true;
    }
}

你还需要在你的控制器方法中表明你想使用这个模型绑定器:

public IHttpActionResult GetValue([ModelBinder(typeof(MemberModelBinder))]QueryParameters param)
  • EDIT-添加了更多代码以转换为类型

如果你想在你的类上使用更多的类型,我只添加了一些最常见的类型(int、bool、DateTime等)解析。字符串是默认值。

public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var model = Activator.CreateInstance(bindingContext.ModelType);
        foreach (var prop in bindingContext.PropertyMetadata)
        {
            // Retrieving attribute
            var attr = bindingContext.ModelType.GetProperty(prop.Key)
                                     .GetCustomAttributes(false)
                                     .OfType<DataMemberAttribute>()
                                     .FirstOrDefault();
            // Overwriting name if attribute present
            var qsParam = (attr != null) ? attr.Name : prop.Key;
            // Setting value of model property based on query string value
            var value = bindingContext.ValueProvider.GetValue(qsParam).AttemptedValue;
            var property = bindingContext.ModelType.GetProperty(prop.Key);
            var propertyType = property.PropertyType;
            object convertedValueObject;
            switch (Type.GetTypeCode(propertyType))
            {
                case TypeCode.Empty:
                case TypeCode.Object:
                case TypeCode.DBNull:
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                case TypeCode.Single:
                    throw new NotImplementedException($"This model binder does not support this type. {propertyType} ");
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    convertedValueObject = ushort.Parse(value);
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    convertedValueObject = int.Parse(value);
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    convertedValueObject = long.Parse(value);
                    break;
                case TypeCode.Double:
                    convertedValueObject = double.Parse(value);
                    break;
                case TypeCode.Decimal:
                    convertedValueObject = decimal.Parse(value);
                    break;
                case TypeCode.DateTime:
                    convertedValueObject = DateTime.Parse(value);
                    break;
                case TypeCode.Boolean:
                    convertedValueObject = bool.Parse(value);
                    break;
           
                default:
                    convertedValueObject = value;
                    break;
            }
            property.SetValue(model, convertedValueObject);

        }
        bindingContext.Model = model;
        return true;
    }

我刚刚遇到这个问题,并在参数类中使用getter返回绑定属性。

所以在你的例子中:

public class QueryParameters
{
    public string cap {get; set;}
    public string Capability
    {
      get { return cap; }
    }
    public string Id { get; set; }
}

现在,在控制器中,您可以引用Capability属性。

public IHttpActionResult GetValue([FromUri]QueryParameters param)    
{
    // Do Something with param.Capability,
    // except assign it a new value because it's only a getter
}

当然,如果对param对象使用反射或对其进行序列化,则会包含cap属性。但不确定为什么任何人都需要对查询参数执行此操作。

我花了几个小时为同一个问题制定了一个稳健的解决方案,而一行代码可以很好地解决这个问题:

myModel.Capability = HttpContext.Current.Request["cap"];