Kasper Holdum
Kasper Holdum

Reputation: 13373

Wrapping/Modifying Html Result

Basically we are in a major hacky situation. We have a few web pages that is linked to from an other site. However, the requirement is that this site has the same layout as the site that linked to us. This was originally done by requesting the original page, scraping the layout, and wrapping out content in their layout.

This was rather simple in Web Forms as we could simply create a subclassed Page, that overrides the Render method and then wrapped anything we produced in the external sites layout. However, now this project is being rewritten in ASP.NET MVC.

How can we get acccess to the HTML result created by the MVC actions, modify them according to our needs and output the modified result to the browser?

Upvotes: 2

Views: 2545

Answers (3)

Kasper Holdum
Kasper Holdum

Reputation: 13373

After having tried tugberk's solution which was insufficient I ended up creating a custom view result:

 public class WrappedViewResult : ViewResult
    {
        private readonly object model;
        public WrapInDtuViewResult()
        {

        }

        public WrapInDtuViewResult(object model)
        {
            this.model = model;
        }


        public override void ExecuteResult(ControllerContext context)
        {

            if (string.IsNullOrWhiteSpace(this.ViewName))
            {
                this.ViewName = context.RouteData.Values["action"].ToString();
            }
            ViewEngineResult result = this.FindView(context);

            context.Controller.ViewData.Model = model;
            ViewDataDictionary viewData = context.Controller.ViewData;
            TempDataDictionary tempData = context.Controller.TempData;

            var writer = new StringWriter();
            ViewContext viewContext = new ViewContext(context, result.View, viewData, tempData, writer);
            result.View.Render(viewContext, writer);


            var content = writer.ToString();


            Scraping scraping = new Scraping();
            if (AppSettings.UseScraping)
            {
                content = scraping.Render(content);
            }
            else
            {
                content = "<html><head><script src='/Scripts/jquery-1.7.1.min.js' type='text/javascript'></script></head><body>" + content + "</body></html>";
            }

            context.HttpContext.Response.Write(content);
        }
    }

Upvotes: 2

tugberk
tugberk

Reputation: 58494

You can use ActionFilterAttribute.OnResultExecuted Method

You can see more sample on ActionFilters here:

http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs

EDIT

There is a great blog post on this topic:

http://weblogs.asp.net/imranbaloch/archive/2010/09/26/moving-asp-net-mvc-client-side-scripts-to-bottom.aspx

To sum it up, you need to tweak your output. As far as I understand you need to use RegEx to get the portion you need to tweak out of the full HTML and you can do that like below:

This is a Helper class:

public class HelperClass : Stream {

    //Other Members are not included for brevity

    private System.IO.Stream Base;

    public HelperClass(System.IO.Stream ResponseStream)
    {
        if (ResponseStream == null)
            throw new ArgumentNullException("ResponseStream");
        this.Base = ResponseStream;
    }

    StringBuilder s = new StringBuilder();

    public override void Write(byte[] buffer, int offset, int count) {

        string HTML = Encoding.UTF8.GetString(buffer, offset, count);

        //In here you need to get the portion out of the full HTML
        //You can do that with RegEx as it is explain on the blog pots link I have given
        HTML += "<div style=\"color:red;\">Comes from OnResultExecuted method</div>";

        buffer = System.Text.Encoding.UTF8.GetBytes(HTML);
        this.Base.Write(buffer, 0, buffer.Length);
    }

}

This is you filter:

public class MyCustomAttribute : ActionFilterAttribute {

    public override void OnActionExecuted(ActionExecutedContext filterContext) {

        var response = filterContext.HttpContext.Response;

        if (response.ContentType == "text/html") {
            response.Filter = new HelperClass(response.Filter);
        }
    }
}

You need to register this on Global.asax file Application_Start method like below:

protected void Application_Start() {

    //there are probably other codes here but I cut them out to stick with point here

    GlobalFilters.Filters.Add(new MyCustomAttribute());
}

Upvotes: 9

Ram Khumana
Ram Khumana

Reputation: 842

Use this blog:

http://blog.maartenballiauw.be/post/2008/06/Creating-an-ASPNET-MVC-OutputCache-ActionFilterAttribute.aspx

public class OutputCache : ActionFilterAttribute
{
public int Duration { get; set; }
public CachePolicy CachePolicy { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    // Client-side caching?
    if (CachePolicy == CachePolicy.Client || CachePolicy == CachePolicy.ClientAndServer)
    {
        if (Duration <= 0) return;
        HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
        TimeSpan cacheDuration = TimeSpan.FromSeconds(Duration);
        cache.SetCacheability(HttpCacheability.Public);
        cache.SetExpires(DateTime.Now.Add(cacheDuration));
        cache.SetMaxAge(cacheDuration);
        cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
    }
}
}

Upvotes: 0

Related Questions