2017-06-15 8 views
1

余計なコメント:これはどこでも明確な答えがまだ見つかりませんでした!ブラケット表記のクエリ文字列からモデルをバインドする

ASP.NET MVCモデルバインディングを使用するには、クエリ文字列を使用するときにドット表記(variableName.propertyName)を使用する必要があります。しかし、jQueryは、variableName[propertyName]=value&のように、GET要求を使用するときにブラケット記法を使用します。 ASP.NET MVCはこの表記を理解できません。

POSTリクエストを発行した場合、投稿されたボディにドット表記を使用するため、ASP.NETはモデルを適切にバインドできます。

大文字と小文字の区別がクエリ文字列内で使用される場合、複雑なオブジェクトであるモデルにASP.NETを強制的にバインドする方法はありますか?

答えて

1

これが理想的な解決策であるかどうかはわかりませんが、IModelBinderの一般的な実装を実装して反射魔法を使って解決しました。この実装の規定では、クエリ文字列のJavaScriptからの要素がcamelCaseにあり、C#のクラスが標準スタイルごとにPascalCaseにあると仮定しています。さらに、それはpublic [set-able]プロパティでのみ機能します。

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) 
{ 
    ... 
} 

この方法の主な没落は、あなたは、ドット表記のクエリ文字列を取得しない場合は、この結合があります標準モデルバインダーに戻ることはないので失敗します。

関連する問題