Reputation: 36078
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
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