2011-08-17 71 views
7

我在捕獲HTML輸出的動作上使用以下篩選器,將其轉換爲字符串,執行一些操作以修改字符串,並返回帶有新的ContentResult串。不幸的是,我一直以空字符串結束。使用控制器動作篩選器捕獲HTML輸出

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; 
     } 
    } 

我牽制是的StreamReader(流).ReadToEnd()返回一個空字符串,但我想不出爲什麼。

任何想法如何解決這個問題?

編輯:我已經將OnActionExecuted更改爲OnResultExecuted,現在它在View生成後調用,但流仍然是空的!

回答

11

我解決了這個由劫持HttpWriter,並將其寫入StringBuilder而不是響應,然後在將響應寫入輸出之前對響應進行任何處理。

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

警告:不建議在操作篩選器中使用實例變量。您無法保證在每個請求中都獲得ActionFilterAttribute的新實例。我將我的代碼從這個答案中解析出來,並且在每秒產生數以千計的請求時遇到了麻煩 - 線(線程)正在越過。按照本文中的建議將實例變量存儲在filterContext.HttpContext.Items中:http:// stackoverflow。com/a/8937793/140449 – jaminto

2

在讀取它之前,嘗試通過設置Position = 0;來倒回流。

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

沒有解決它,但它也使我注意到該位置已經是0.所以它看起來像流必須是空的....我不知道爲什麼 – yoozer8

0

你可以在OnActionExectuted方法中驗證流是否爲NULL?我不知道流變量的狀態被存儲整個過程..

你爲什麼不嘗試讓流出來的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

嗯,我認爲這將是答案,但我也有同樣的問題。我從filterContext.HttpContext.Response.Filter獲得的流長度爲0. – yoozer8

1

我想我已經開發了一個很好的方法來做到這一點。

  • 與自定義一個
  • 該濾波器需要委託接受一個流
  • 此委託,因此抽象方法被稱爲該流的靠近一個抽象方法更換效應初探過濾,即當所有的HTML都可用時
  • 覆蓋OnClose方法並隨意使用流。

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(); 
     } 
    } 
} 

然後你可以從這個推導到另一個屬性來訪問數據流,並獲得HTML:

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

問題是關於更新結果 - 儘管解決方法不可行嗎? – Gaz

+0

@Gaz應該很簡單,可以修改,以使其工作。很久以前,所以不記得對不起! –

+0

@Gaz,這應該讓OP在正確的方向。問題是'filterContext.RequestContext.HttpContext.Response'中的默認流不支持讀取,因此您必須將流替換爲允許讀取的流。 – ps2goat