Захват вывода HTML с помощью фильтра действий контроллера - PullRequest
7 голосов
/ 17 августа 2011

У меня есть следующий фильтр для действия, чтобы захватить вывод 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 (поток) .ReadToEnd () возвращает пустую строку, но я не могу понять, почему.

Есть идеи как это исправить?

EDIT : я изменил OnActionExecuted на OnResultExecuted, и теперь он вызывается после того, как представление было сгенерировано, но поток по-прежнему пуст!

Ответы [ 4 ]

11 голосов
/ 17 августа 2011

Я решил эту проблему, взломав 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);
        }
    }
2 голосов
/ 17 августа 2011

Попробуйте перемотать поток в начало, установив 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;
}
1 голос
/ 15 августа 2014

Я думаю, что разработал довольно хороший способ сделать это.

  • Заменить фильтр отклика на собственный
  • Этот фильтр переносит делегата в абстрактный метод, который принимает поток
  • Это делегат, и, следовательно, абстрактный метод вызывается при закрытии потока, т. Е. Когда доступен весь 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 голосов
/ 17 августа 2011

Можете ли вы проверить, что поток не равен NULL в OnActionExectuted-методе? Я не уверен, что состояние переменной потока сохраняется в процессе ..

Почему бы вам не попытаться получить поток из 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;
}
...