使用控制器操作过滤器捕获 HTML 输出

发布于 2024-11-30 02:10:55 字数 1154 浏览 2 评论 0原文

我在一个操作上设置了以下过滤器来捕获 HTML 输出,将其转换为字符串,执行一些操作来修改字符串,并返回带有新字符串的 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,现在在生成视图后调用它,但流仍然是空的!

I've got the following filter in place on an action to capture the HTML output, convert it to a string, do some operations to modify the string, and return a ContentResult with the new string. Unfortunately, I keep ending up with an empty string.

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've pinned down that StreamReader(stream).ReadToEnd() returns an empty string, but I can't figure out why.

Any ideas how to fix this?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

习ぎ惯性依靠 2024-12-07 02:10:55

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

上面的代码使用 HttpContext 来避免线程错误 - 请参阅 jaminto 的评论

private class RenderFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        StringBuilder sb = new StringBuilder();
        StringWriter sw = new StringWriter(sb);
        HtmlTextWriter tw = new HtmlTextWriter(sw);
        HttpWriter output = (HttpWriter)filterContext.RequestContext.HttpContext.Response.Output;
        filterContext.HttpContext.Items["sb"] = sb;
        filterContext.HttpContext.Items["output"] = output;
        filterContext.RequestContext.HttpContext.Response.Output = tw;
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        string response = filterContext.HttpContext.Items["sb"].ToString();
        //response processing
        ((HttpWriter)filterContext.HttpContext.Items["output"]).Write(response);
    }
}

I solved this by hijacking the HttpWriter, and having it write into a StringBuilder rather than the response, and then doing whatever needs to be done to/with 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);
    }
}

Above code using the HttpContext to avoid threading errors - see jaminto's comment

private class RenderFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        StringBuilder sb = new StringBuilder();
        StringWriter sw = new StringWriter(sb);
        HtmlTextWriter tw = new HtmlTextWriter(sw);
        HttpWriter output = (HttpWriter)filterContext.RequestContext.HttpContext.Response.Output;
        filterContext.HttpContext.Items["sb"] = sb;
        filterContext.HttpContext.Items["output"] = output;
        filterContext.RequestContext.HttpContext.Response.Output = tw;
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        string response = filterContext.HttpContext.Items["sb"].ToString();
        //response processing
        ((HttpWriter)filterContext.HttpContext.Items["output"]).Write(response);
    }
}
沦落红尘 2024-12-07 02:10:55

在阅读流之前,尝试通过设置 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;
}

Try rewinding the stream to the beginning by setting Position = 0; before you read 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;
}
命比纸薄 2024-12-07 02:10:55

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

  • 将响应过滤器替换为自定义过滤器
  • 该过滤器接受一个抽象方法的委托,该方法接受一个流
  • 这是委托,因此抽象方法在流关闭时被调用,即当所有 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
    }
}

I think I've developed a pretty good way to do this.

  • Replace the Reponse Filter with a custom one
  • This filter takes a delegate to an abstract method which takes a stream
  • This the delegate, and hence the abstract method are called on the close of the stream, i.e. when all the HTML is available
  • Override the OnClose method and play with 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 derive from this to another attribute to access the stream and get the HTML:

public class MyAttribute : ReadOnlyActionFilterAttribute
{
    protected override void OnClose(Stream stream)
    {
        var html = new HtmlDocument();
        html.Load(stream);
        // play with html
    }
}
一花一树开 2024-12-07 02:10:55

您可以验证 OnActionExectulated 方法中的流不为 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;
}

Can you verify that stream is not NULL in the OnActionExectuted-method? I'm not sure the state of the stream-variable is being stored through the process..

Why don't you try to get the stream out of the 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文