Capturing HTML output using a controller action filter

I have a filter in place to capture HTML output, convert it to a string, perform some operations to modify the string, and return a ContentResult with a new string. Sorry, I'm ending an empty line.

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

I bound this StreamReader (stream) .ReadToEnd () returns an empty string, but I can not understand why.

Any ideas how to fix this?

EDIT : I changed OnActionExecuted to OnResultExecuted, and now it is called after the View has been generated, but the stream is still empty!

+7
source share
4 answers

I solved this by capturing the HttpWriter and writing it to a StringBuilder , not the response, and then doing everything that needs to be done for the response before writing it to the 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); } } 
+11
source

Try rewinding the stream to the beginning by setting Position = 0; before reading it.

 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; } 
+2
source

I think I have developed a pretty good way to do this.

  • Replace the response filter with a custom one.
  • This filter accepts a delegate of the abstract method that accepts the stream.
  • This delegate and therefore the abstract method is called when the stream closes, i.e. when all HTML is available
  • Override the OnClose method and play the stream as you like.

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

You can then extract from this other attribute to access the stream and retrieve the HTML:

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

Is it possible to verify that the stream is not NULL in the OnActionExectuted method? I am not sure if the state of the thread variable is stored in the process.

Why don't you try to infer the stream from the Context filter:

 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
source

All Articles