2014-01-25 3 views
23

Ich habe ein Attribut namens Log, das versucht, Inhalt der Anfrage und Antwort in eine Textdatei zu protokollieren. Ich habe das über meinen Controller gelegt, um alle Aktionen abzudecken. In LogAttribute lese ich Inhalt als String (ReadAsStringAsync), damit ich den Anfragetext nicht verliere.Web API Anfrage Inhalt ist leer in Aktion Filter

Auf der anderen Seite habe ich FromBody-Attribut vor meine Aktion Parameter-Klasse gesetzt, um seine Vorteile zu nutzen.

Das Problem ist der Inhalt ist immer entweder in ActionExecuting oder ActionExecuted leer.

Ich denke, das ist, weil FromBody vor meinem Log-Attribut im Gegensatz zu ihrer Reihenfolge im Code ausgeführt wird. Und wieder denke ich daran, dass die beste Action/Controller-Übereinstimmung für die Anfrage gemäß den Aktionsparametern (Route Processing) gefunden wird. Danach wird mein Anfragetext gelöscht, da der Anfragetext in WebApi nicht gepuffert ist.

Ich möchte wissen, ob es eine Möglichkeit gibt, die Laufzeitfolge des FromBody-Attributs und meines Log-Attributs zu ändern? oder etwas anderes, das das Problem löst! Ich sollte erwähnen, dass ich FromBody nicht entfernen und HttpRequestMessage anstelle meines Modells oder etwas in der Art verwenden möchte.

+1

dieses neben Ihrer async Handhabung ist falsch. Das Verwenden von .Result ist gefährlich, das sollten Sie, wenn möglich, vermeiden. Überschreiben Sie lieber die OnActionExecutingAsync-Methode, mit der Sie warten können. –

+0

@LukasK eigentlich wurde das nur geschrieben, um meine Frage zu erklären. aber du hast Recht. –

Antwort

25

Der Anfragetext ist ein nicht zurückspulbarer Stream. es kann nur einmal gelesen werden. Der Formatierer hat den Stream bereits gelesen und das Modell aufgefüllt. Wir können den Stream im Aktionsfilter nicht erneut lesen.

Sie könnten versuchen:

public class LogAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     var myModel = actionContext.ActionArguments["myModel"]; 

    } 

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 
    { 
     var myModel = actionContext.ActionArguments["myModel"]; 
    } 
} 

Eigentlich ActionArguments ist nur ein Wörterbuch, das können wir Schleife obwohl es, wenn wir brauchen hartkodierte Parameternamen ("myModel") zu vermeiden. Wenn wir einen generischen Aktionsfilter erstellen, der an einer Klasse ähnlicher Objekte für bestimmte Anforderungen arbeiten muss, könnten unsere Modelle eine Schnittstelle implementieren => wissen, welches Argument das Modell ist, an dem wir arbeiten müssen, und wir können die Methoden aufrufen die Schnittstelle.

Beispielcode:

public class LogAttribute : ActionFilterAttribute 
    { 
     public override void OnActionExecuting(HttpActionContext actionContext) 
     { 
      foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is ILogable))) 
      { 
       ILogable model = argument as ILogable;//assume that only objects implementing this interface are logable 
       //do something with it. Maybe call model.log 
      } 
     } 

     public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 
     { 
      foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is ILogable))) 
      { 
       ILogable model = argument as ILogable;//assume that only objects implementing this interface are logable 
       //do something with it. Maybe call model.log 
      } 
     } 
    } 
+0

Danke, Bruder. Was soll ich tun, wenn ich mehrere Parameter habe und ihren Namen nicht kenne? In dieser Lösung sollte ich einen Vertrag festlegen, dass "immer mein Parametername myModel" ist? Ja? –

+0

@Reza Ahmadi: 'ActionArguments' ist nur ein Wörterbuch. Sie können es durchschleifen. –

+0

@Reza Ahmadi: Normalerweise, wenn Sie diese Art von Filter für eine bestimmte Aktion gegen eine Klasse ähnlicher Objekte anwenden möchten. Wenn Ihre Modelle eine Schnittstelle implementieren, können Sie beim Durchlaufen des Wörterbuchs darauf hinweisen, dass dies das Modell ist, an dem Sie arbeiten müssen. (vielleicht Methoden über die Schnittstelle aufrufen) –

9

Dieser Ansatz für mich gearbeitet:

using (var stream = new MemoryStream()) 
{ 
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"]; 
    context.Request.InputStream.Seek(0, SeekOrigin.Begin); 
    context.Request.InputStream.CopyTo(stream); 
    string requestBody = Encoding.UTF8.GetString(stream.ToArray()); 
} 

für mich kehrten die json Darstellung meines Aktionsparameter Objekt Auslösen der Protokollierung oder Ausnahmefall.

als akzeptierte Antwort gefunden here