0

Ich habe angefangen, in ein seltsames Problem zu laufen.BindModel wird vor ActionFilterAttribute ausgeführt

Ich habe ASP.NET-Projekt, in dem ich eine API haben, die POST-Parameter übernimmt. Da ich eine Schnittstelle verwende, habe ich einen benutzerdefinierten Deserializer verwendet, um das POST-Objekt zu lesen. Es funktionierte bis in die letzten paar Tage. Aber eines Tages bekam ich 500 - Interner Serverfehler, der sagt "Kann eine Instanz einer Schnittstelle nicht schaffen. ... durch CreateModel". Zu dieser Zeit benutzte ich die PostMan App. Da es praktisch keine Änderung im Code gab, dachte ich, dass PostMan App möglicherweise beschädigt wurde.

Ich war mir nicht sicher, also hatte ich dieselbe Abfrage auf Fiddler versucht und es hat gut funktioniert. Jetzt, nach 3-4 Tagen, hörte Fiddler auch auf, mit demselben Fehler zu arbeiten.

Nach dem Graben durch, fand ich, dass irgendwie ‚BindModel‘ hat damit begonnen, die Ausführung vor Action sein kann. Ich bin mir nur nicht sicher, wie es möglich ist. Gibt es einen Workaround, um diese Situation zu überwinden? Mein Post http Aufruf tritt nur nicht JsonFilter der OnActionExecuting Methode

Fehler msg:

[Missing:. Es kann keine Instanz einer Schnittstelle erstellen]
System.RuntimeTypeHandle.CreateInstance (...)
System.RuntimeType.CreateInstanceSlow (...)
System.Activator.CreateInstance (...)
System.Activator.CreateInstance (...)
System.Web.Mvc.DefaultModelBinder.CreateMod el (...)

[MissingMethodException: Eine Instanz einer Schnittstelle kann nicht erstellt werden. Objekttyp 'MyCommonObj.IMyInterface'.]
System.Web.Mvc.DefaultModelBinder.CreateModel (...)
System.Web.Mvc.DefaultModelBinder.BindComplexModel (...)
System.Web.Mvc. DefaultModelBinder.BindModel (...)

Code Snippet:

public class JsonFilter : ActionFilterAttribute { 
    public string Parameter { get; set; } 
    public Type JsonDataType { get; set; } 

    public override void OnActionExecuting(ActionExecutingContext filterContext) { 
     if (filterContext.HttpContext.Request.ContentType.Contains("application/json")) { 
      string inputContent; 
      filterContext.HttpContext.Request.InputStream.Position = 0; 
      using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream)) { 
       inputContent = sr.ReadToEnd(); 
      }; 

      var jsonSerializerSettings = new JsonSerializerSettings() { 
       TypeNameHandling = TypeNameHandling.All 
      }; 

      if (JsonDataType == typeof(MyClass)) { 
       var result = JsonConvert.DeserializeObject<MyClass>(inputContent, jsonSerializerSettings); 
       filterContext.ActionParameters[Parameter] = result; 
      } 
      else { 
       throw new NotImplementedException(); 
      } 
     } 
    } 
} 

[HttpPost] 
[JsonFilter(Parameter = "config", JsonDataType = typeof(MyClass))] 
public ActionResult ExecuteApi(MyClass config) { 
    var result = DoSomething(config); 
    return Json(result); 
} 

public interface IMyInterface { 
    string GetValue(); 
} 

public class MyDerivedClass : IMyInterface { 
    public string Value { get; set; } 

    public MyDerivedClass(string v) { 
     Value = v; 
    } 

    public string GetValue() { return Value; } 
} 

public class Query { 
    [JsonProperty(TypeNameHandling = TypeNameHandling.All)] 
    public IMyInterface type { get; set; } 

    public Query() {} 
} 

public class MyClass { 
    List<Query> myList { get; set; } 

    public MyClass() {} 
} 

Antwort

0

Aktionsfilter immer Lauf nach dem Vorbild Bindemittel. Wenn Sie einen Filter benötigen, der vor dem Modellbinder ausgeführt wird, sollten Sie IAuthorizationFilter verwenden.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] 
public class JsonFilter : Attribute, IAuthorizationFilter 
{ 
    public string Parameter { get; set; } 
    public Type JsonDataType { get; set; } 

    public void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext.HttpContext.Request.ContentType.Contains("application/json")) 
     { 
      string inputContent; 
      filterContext.HttpContext.Request.InputStream.Position = 0; 
      using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream)) 
      { 
       inputContent = sr.ReadToEnd(); 
      }; 

      var jsonSerializerSettings = new JsonSerializerSettings() 
      { 
       TypeNameHandling = TypeNameHandling.All 
      }; 

      if (JsonDataType == typeof(MyClass)) 
      { 
       var result = JsonConvert.DeserializeObject<MyClass>(inputContent, jsonSerializerSettings); 
       filterContext.ActionParameters[Parameter] = result; 
      } 
      else 
      { 
       throw new NotImplementedException(); 
      } 
     } 
    } 
} 
+0

Dieser Ansatz funktioniert nicht, da ich JsonDataType lesen und deserialisiertes Konfigurationsobjekt zurück zur ExecuteApi() -Methode übergeben muss. Außerdem wird diese Methode für alle Aufrufe aufgerufen. Wenn möglich, möchte ich diesen Methodenaufruf auf bestimmte APIs beschränken. –