Главная > ASP.NET > ScriptManager для ASP.NET MVC. Третий вариант.

ScriptManager для ASP.NET MVC. Третий вариант.

Очередная вариация на тему ScriptManager’а, это скорее отрефакторенный вариант второго решения. В прошлый раз мы использовали HttpModule, но из-за особенностей обработки запросов модулем его использование влечет за собой накладные расходы, поскольку любой запрос, будь то файл стилей или файл с клиентскими скриптами или же изображение, проходит через всю цепочку модулей, которые зарегистрированы в ASP.NET приложении. В предыдущем скрипт-менеджере мы делали проверку, что текущий запрос — это запрос именно html-страницы, и если это так, то мы запускали обработку html. Однако, по умолчанию, в приложениях ASP.NET MVC весь выходной html-код генерится контроллером и вьюхой, а если это и ваш случай, то можно сделать все проще, мы будем использовать ActionFilterAttribute.

Про ActionFilterAttribute можете почитать в сети, я лишь скажу, что с помощью таких атрибутов можно вмешиваться в процесс обработки запросов контроллерами ASP.NET MVC, их можно «навешивать» как на сам класс контроллера, так и на экшн.

Точно так же, как и в случае с модулем, нам нужно перехватить рендеринг в определенный момент, для этого подходит метод «OnActionExecuting». Каждый Action контроллера, который возвращает наследника от ViewResultBase, может содержать скрипты, ссылки на таблицы стилей и прочие вещи, которые мы хотим поместить в head html-документа. Есть одна особенность — контроллеры, помимо обычных экшенов, могут содержать так называемые ChildAction’ы, ссылки на такие экшены обычно размещаются во вьюхе, которую возвращает обычный экшен, вот именно такие Child’овые экшены нам как раз перехватывать не нужно, если вы будете это делать, то получите сообщение об ошибке, вместо ожидаемой страницы. Итак, код нашего атрибута прост и практически не отличается от кода прошлого HttpModule.

    public class OverwriteOutputAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);
            if (filterContext.IsChildAction || !(filterContext.Result is ViewResultBase))
            {
                return;
            }

            var httpContextBase = filterContext.RequestContext.HttpContext;
            httpContextBase.Response.Filter = new ResponseFilter(httpContextBase.Request, httpContextBase.Response);
        }

        /* Альтернативный вариант, но раскомменченный лучше, т.к. обрабатывает только экшены ViewResultBase.
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            if (filterContext.IsChildAction)
            {
                return;
            }

            var httpContextBase = filterContext.RequestContext.HttpContext;
            httpContextBase.Response.Filter = new ResponseFilter(httpContextBase.Request, httpContextBase.Response);
        }
        */

        private class ResponseFilter : MemoryStream
        {
            private readonly Stream outputStream;
            private readonly HttpRequestBase request;
            private readonly HttpResponseBase response;
            private readonly StringBuilder outputHtml = new StringBuilder();
            private Boolean isRewrited;

            public ResponseFilter(HttpRequestBase httpRequestWrapper, HttpResponseBase httpResponseWrapper)
            {
                request = httpRequestWrapper;
                response = httpResponseWrapper;
                outputStream = response.Filter;
            }

            public override void Write(Byte[] buffer, Int32 offset, Int32 count)
            {
                outputHtml.Append(response.Output.Encoding.GetString(buffer, offset, count));
            }

            public override void Close()
            {
                if (!isRewrited)
                {
                    RewriteOutput();
                    isRewrited = true;
                }

                base.Close();
            }

            private void RewriteOutput()
            {
                if (request.IsAjaxRequest())
                {
                    outputHtml
                        .Append(HtmlCustomHelper.LoadCss())
                        .Append(HtmlCustomHelper.LoadJs());
                }
                else
                {
                    outputHtml
                        .Replace(Constants.Web.CssZoneMark, HtmlCustomHelper.LoadCss())
                        .Replace(Constants.Web.ScriptsZoneMark, HtmlCustomHelper.LoadJs());
                }

                var outputHtmlBytes = response.Output.Encoding.GetBytes(outputHtml.ToString());
                outputStream.Write(outputHtmlBytes, 0, outputHtmlBytes.Length);
            }
        }
    }

Ну и пример использования:

    [OverwriteOutput]
    public abstract class BaseController : Controller
    {
        ...
    }

Такое решение более правильное, поскольку мы не создаем никаких дополнительных модулей и не делаем дополнительных проверок на каждый Request.

  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.