使用控制器动作filter捕获HTML输出

我在捕获HTML输出的动作上有以下filter,将其转换为字符串,执行一些操作以修改字符串,并返回带有新字符串的ContentResult。 不幸的是,我一直以一个空字符串结束。

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(stream).ReadToEnd()返回一个空字符串,但我无法弄清楚原因。

任何想法如何解决这一问题?

编辑 :我已经将OnActionExecuted更改为OnResultExecuted,现在在生成View之后调用它,但是流仍然是空的!

我通过劫持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); } } 

尝试通过设置Position = 0;将流重绕到开头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; } 

我想我已经开发了一个非常好的方法来做到这一点。

  • 用自定义filter替换响应filter
  • 此filter将委托转移到采用流的抽象方法
  • 这个委托,因此抽象方法在流的结束时被调用,即当所有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 } } 

你可以在OnActionExectuted方法中validation流是不是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; }