多余的评论:我无法相信我无法在任何地方找到明确的答案!
使用ASP.NET MVC模型绑定时,在使用查询字符串时需要使用点表示法(variableName.propertyName
)。但是,jQuery在使用GET请求时将使用括号表示法,例如variableName[propertyName]=value&
。 ASP.NET MVC无法理解这种表示法。
如果我发出POST请求,ASP.NET可以正确绑定模型,因为它在发布的正文中使用点表示法。
在查询字符串中使用括号表示法时,有没有办法强制ASP.NET绑定到作为复杂对象的模型?
答案 0 :(得分:2)
我不确定这是否是理想的解决方案,但我通过实现IModelBinder
的通用实现,使用一些反思魔法解决了这个问题。这个实现的规定是它假设查询字符串中的JavaScript元素在camelCase中,而C#中的类在每个标准样式的PascalCase中。此外,它仅适用于公共[可设置]属性。以下是我的实施方式:
public class BracketedQueryStringModelBinder<T> : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite);
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (var p in properties)
{
if (!IsNullable(p.PropertyType))
{
object val = TryGetValueType(p.PropertyType, bindingContext, p.Name);
if (val != null)
{
values.Add(p.Name, val);
}
}
else
{
object val = GetRefernceType(p.PropertyType, bindingContext, p.Name);
values.Add(p.Name, val);
}
}
if (values.Any())
{
object boundModel = Activator.CreateInstance<T>();
foreach (var p in properties.Where(i => values.ContainsKey(i.Name)))
{
p.SetValue(boundModel, values[p.Name]);
}
return boundModel;
}
return null;
}
private static bool IsNullable(Type t)
{
if (t == null)
throw new ArgumentNullException("t");
if (!t.IsValueType)
return true;
return Nullable.GetUnderlyingType(t) != null;
}
private static object TryGetValueType(Type type, ModelBindingContext ctx, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
key = ConvertToPascalCase(key);
ValueProviderResult result = ctx.ValueProvider.GetValue(string.Concat(ctx.ModelName, "[", key, "]"));
if (result == null && ctx.FallbackToEmptyPrefix)
result = ctx.ValueProvider.GetValue(key);
if (result == null)
return null;
try
{
object returnVal = result.ConvertTo(type);
ctx.ModelState.SetModelValue(key, result);
return returnVal;
}
catch (Exception ex)
{
ctx.ModelState.AddModelError(ctx.ModelName, ex);
return null;
}
}
private static object GetRefernceType(Type type, ModelBindingContext ctx, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
key = ConvertToPascalCase(key);
ValueProviderResult result = ctx.ValueProvider.GetValue(string.Concat(ctx.ModelName, "[", key, "]"));
if (result == null && ctx.FallbackToEmptyPrefix)
result = ctx.ValueProvider.GetValue(key);
if (result == null)
return null;
try
{
object returnVal = result.ConvertTo(type);
ctx.ModelState.SetModelValue(key, result);
return returnVal;
}
catch (Exception ex)
{
ctx.ModelState.AddModelError(ctx.ModelName, ex);
return null;
}
}
private static string ConvertToPascalCase(string str)
{
char firstChar = str[0];
if (char.IsUpper(firstChar))
return char.ToLower(firstChar) + str.Substring(1);
return str;
}
}
然后在您的控制器中,您可以像这样使用它:
[HttpGet]
public ActionResult myAction([ModelBinder(typeof(BracketedQueryStringModelBinder<MyClass>))] MyClass mc = null)
{
...
}
此方法的主要缺点是,如果您以点表示法获得查询字符串,则此绑定将失败,因为它不会恢复为标准模型绑定器。