TruMan1
TruMan1

Reputation: 36078

Inject HTML or scripts into MVC page lifecycle?

Is there a way to intercept the page lifecycle from an MVC controller and inject code somewhere on the page or DOM? I would like to add HTML, CSS, and JavaScript onto the page header and footer, but without adding them to the _Layout view which is embedded in an assembly somewhere. SO I would like to do this from a controller of the Global.asax / Application_Start. Is this possible?

Upvotes: 2

Views: 1017

Answers (1)

Amir Yonatan
Amir Yonatan

Reputation: 727

I did something similar to that.

First, you need to add placeholder inside your layout files (where you want to inject the code)

Second, you need to add a global ActionFilterAttribute in your global.asax. In the FilterAttribute, override filter only the OnActionExecuted and filter only ViewResults.

Third, you need to change the Response.Filter, with a new filter that will search for the placeholders inside the html code, and replace it with your own.

I wrote exactly that, I added a way to declare Styles & Scripts anywhere in the code, and when the result is done, the resources are injected into the head tag.

public class DynamicHeaderHandler : ActionFilterAttribute
{
    private static readonly byte[] _headClosingTagBuffer = Encoding.UTF8.GetBytes("</head>");
    private static readonly byte[] _placeholderBuffer = Encoding.UTF8.GetBytes(DynamicHeader.SectionPlaceholder);

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            filterContext.HttpContext.Response.Filter = new ResponseFilter(filterContext.HttpContext.Response.Filter);
        }

        base.OnActionExecuted(filterContext);
    }

    private class ResponseFilter : MemoryStream
    {
        private readonly Stream _outputStream;

        public ResponseFilter(Stream aOutputStream)
        {
            _outputStream = aOutputStream;
        }

        public override void Close()
        {
            _outputStream.Flush();
            _outputStream.Close();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            var maxIndex = IndexOfSubArray(buffer, _headClosingTagBuffer);
            var index = IndexOfSubArray(buffer, _placeholderBuffer, maxIndex);

            if (index == -1)
            {
                _outputStream.Write(buffer, offset, count);
            }
            else
            {
                var html = DynamicHeader.GetHtml();
                var injectBytes = Encoding.UTF8.GetBytes(html);
                var newBuffer = buffer.Take(index).Concat(injectBytes).Concat(buffer.Skip(index + _placeholderBuffer.Length)).ToArray();
                _outputStream.Write(newBuffer, 0, newBuffer.Length);
            }
        }

        private static int IndexOfSubArray(byte[] aBuffer, byte[] aSubBuffer, int aMaxIndex = int.MaxValue)
        {
            if (aBuffer.Length < aSubBuffer.Length)
            {
                return -1;
            }

            for (var outerIndex = 0; outerIndex < aBuffer.Length && outerIndex < aMaxIndex; ++outerIndex)
            {
                var isEqual = true;
                for (int subInnerIndex = 0, innerIndex = outerIndex; subInnerIndex < aSubBuffer.Length && innerIndex < aBuffer.Length; ++subInnerIndex, ++innerIndex)
                {
                    if (aBuffer[innerIndex] != aSubBuffer[subInnerIndex])
                    {
                        isEqual = false;
                        break;
                    }
                }

                if (isEqual)
                {
                    return outerIndex;
                }
            }

            return -1;
        }
    }
}

I attached the ActionFilterAttribute, which is probably whats relevant to you.

If you want to see the entire code, you can visit here: Add Styles & Scripts Dynamically

Upvotes: 2

Related Questions