2011-08-17 17 views
7

Ho il seguente filtro in atto su un'azione per acquisire l'output HTML, convertirlo in una stringa, eseguire alcune operazioni per modificare la stringa e restituire ContentResult con il nuovo stringa. Sfortunatamente continuo a finire con una corda vuota.Acquisizione dell'output HTML con un filtro di azione del controller

private class UpdateFilter : ActionFilterAttribute 
    { 
     private Stream stream; 

     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      stream = filterContext.HttpContext.Response.Filter; 
      stream = new MemoryStream(); 
      filterContext.HttpContext.Response.Filter = stream; 
     } 

     public override void OnResultExecuted(ResultExecutedContext filterContext) 
     { 
      StreamReader responsereader = new StreamReader(filterContext.HttpContext.Response.Filter); //empty stream? why? 
      responsereader.BaseStream.Position = 0; 
      string response = responsereader.ReadToEnd(); 
      ContentResult contres = new ContentResult(); 
      contres.Content = response; 
      filterContext.Result = contres; 
     } 
    } 

ho immobilizzato che StreamReader (stream) .ReadToEnd() restituisce una stringa vuota, ma non riesco a capire perché.

Qualche idea su come risolvere questo problema?

EDIT: Ho modificato l'OnActionExecuted su OnResultExecuted e ora viene chiamato dopo che la Vista è stata generata, ma lo stream è ancora vuoto!

risposta

11

ho risolto questo dirottando HttpWriter e facendolo scrivere in un StringBuilder piuttosto che nella risposta, e poi facendo tutto ciò che deve essere fatto a/con la risposta prima di scriverlo sull'output.

private class UpdateFilter : ActionFilterAttribute 
    { 
     private HtmlTextWriter tw; 
     private StringWriter sw; 
     private StringBuilder sb; 
     private HttpWriter output; 

     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      sb = new StringBuilder(); 
      sw = new StringWriter(sb); 
      tw = new HtmlTextWriter(sw); 
      output = (HttpWriter)filterContext.RequestContext.HttpContext.Response.Output; 
      filterContext.RequestContext.HttpContext.Response.Output = tw; 
     } 

     public override void OnResultExecuted(ResultExecutedContext filterContext) 
     { 
      string response = sb.ToString(); 
      //response processing 
      output.Write(response); 
     } 
    } 
+12

un avviso: non è consigliabile utilizzare variabili di istanza nei filtri azione. non ti è garantito di ottenere una nuova istanza di ActionFilterAttribute su ogni richiesta. Ho basato il mio codice su questa risposta e mi sono messo nei guai quando è andato in produzione con migliaia di richieste al secondo - i fili (thread) si sono incrociati. memorizza le variabili di istanza in filterContext.HttpContext.Items come suggerito in questo post: http: // stackoverflow.it/a/8937793/140449 – jaminto

2

Provare a riavvolgere il flusso all'inizio impostando Position = 0; prima di leggerlo.

public override void OnActionExecuted(ActionExecutedContext filterContext) 
{ 
    stream.Position = 0; 
    string response = new StreamReader(stream).ReadToEnd(); 
    ContentResult contres = new ContentResult(); 
    contres.Content = response; 
    filterContext.Result = contres; 
} 
+0

che non risolvere il problema, ma mi ha portato a notare che la Posizione è già 0. Quindi sembra che il flusso deve essere vuoto .... Mi chiedo perché – yoozer8

0

È possibile verificare che il flusso non sia NULL nel metodo OnActionExectuted? Non sono sicuro che lo stato del flusso variabile viene memorizzata attraverso il processo ..

Perché non si cerca di ottenere il flusso fuori dalla filterContext:

public override void OnActionExecuted(ActionExecutedContext filterContext) 
{ 
    var stream = filterContext.HttpContext.Response.Filter; 
    string response = new StreamReader(stream).ReadToEnd(); 
    ContentResult contres = new ContentResult(); 
    contres.Content = response; 
    filterContext.Result = contres; 
} 
+0

Bene, ho pensato che questa sarebbe stata la risposta, ma ho lo stesso problema. Lo stream che ottengo da filterContext.HttpContext.Response.Filter ha una lunghezza 0. – yoozer8

1

Penso di aver sviluppato un buon modo per farlo.

  • sostituire il filtro Reponse con uno personalizzato
  • Questo filtro prende un delegato ad un metodo astratto che prende un flusso
  • Questa delegato, e quindi il metodo astratto sono chiamate chiusura del flusso , ovvero quando tutto l'HTML è disponibile
  • Ignora il metodo OnClose e gioca con lo streaming come preferisci.

public abstract class ReadOnlyActionFilterAttribute : ActionFilterAttribute 
{ 
    private delegate void ReadOnlyOnClose(Stream stream); 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     filterContext.HttpContext.Response.Filter = new OnCloseFilter(
      filterContext.HttpContext.Response.Filter, 
      this.OnClose); 
     base.OnActionExecuting(filterContext); 
    } 

    protected abstract void OnClose(Stream stream); 

    private class OnCloseFilter : MemoryStream 
    { 
     private readonly Stream stream; 

     private readonly ReadOnlyOnClose onClose; 

     public OnCloseFilter(Stream stream, ReadOnlyOnClose onClose) 
     { 
      this.stream = stream; 
      this.onClose = onClose; 
     } 

     public override void Close() 
     { 
      this.Position = 0; 
      this.onClose(this); 
      this.Position = 0; 
      this.CopyTo(this.stream); 
      base.Close(); 
     } 
    } 
} 

si può quindi derivare da questo ad un altro attributo per accedere al flusso e ottenere il codice HTML:

public class MyAttribute : ReadOnlyActionFilterAttribute 
{ 
    protected override void OnClose(Stream stream) 
    { 
     var html = new HtmlDocument(); 
     html.Load(stream); 
     // play with html 
    } 
} 
+0

La domanda riguarda l'aggiornamento del risultato - non è possibile con la soluzione? – Gaz

+0

@Gaz Dovrebbe essere abbastanza semplice da modificare per farlo funzionare. Un po 'di tempo fa, quindi, non ricordo che mi dispiace! –

+0

@Gaz, questo dovrebbe portare l'OP nella giusta direzione. Il problema è che il flusso predefinito in 'filterContext.RequestContext.HttpContext.Response' non supporta la lettura, quindi devi sostituire lo stream con uno che consenta la lettura. – ps2goat

Problemi correlati