5

I have a controller action that returns a large amount of dynamic JavaScript (gets served once to the client) and I already have GZip compression enabled. One thing I'd like to do is read the executed result stream and apply JS minification on it.

Is it possible to do this using an Action Filter Attribute. I think my question boils down to - Assuming my minifier takes a string of JavaScript is there a way to pull the executed result as a string out of View(view).ExecuteResult(ControllerContext) ?

James Hughes
  • 6,194
  • 4
  • 31
  • 44
  • Have you considered minify on publish? You can do this using ms build, this is the one we use http://code.google.com/p/antix-software/wiki/MSBuild – Anthony Johnston Jul 15 '10 at 08:09
  • It's a no go these results are actually SPARK views that are simply JavaScript with embedded C# (localisation, default values etc). So they need dynamically executed and can;t be pre-minified. It needs to be post execute. – James Hughes Jul 15 '10 at 09:29

1 Answers1

4

I think the YUI Compressor for .NET will do exactly what you need.

http://yuicompressor.codeplex.com/

EDIT: Above is wrong as I misread the question. The code below will install a response filter allowing you to manipulate the output, in this case it just removes newline characters.

Hope this helps.

[HandleError]
public class HomeController : Controller
{
    [Minify]
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }
}

public class Minify : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //You can check if the content type is CSS/JS here and prevent the filter running on HTML pages 

        filterContext.HttpContext.Response.Filter = new MinifyFilter(filterContext.HttpContext.Response.Filter);

        base.OnActionExecuting(filterContext);
    }
}

public class MinifyFilter : MemoryStream
{
    private StringBuilder outputString = new StringBuilder();
    private Stream outputStream = null;

    public MinifyFilter(Stream outputStream)
    {
        this.outputStream = outputStream;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        outputString.Append(Encoding.UTF8.GetString(buffer));
    }

    public override void Close()
    {
        //Call the minifier here, your data is in outputString
        string result = outputString.ToString().Replace(Environment.NewLine, string.Empty);

        byte[] rawResult = Encoding.UTF8.GetBytes(result);
        outputStream.Write(rawResult, 0, rawResult.Length);

        base.Close();
        outputStream.Close();
    }
}
amarsuperstar
  • 1,783
  • 1
  • 17
  • 22
  • Yes it will perform the minification but that wasn't my question. Assuming I have a result like `View()` I want to be able to take that stream and convert it to string, minify and write it back to another result (or directly to the response if necessary) – James Hughes Jul 15 '10 at 13:04
  • Aah, sorry I misread the question. You need a response filter which you can hook up within an ActionFilter Attribute, similar to this: http://stackoverflow.com/questions/1640909/asp-net-mvc-response-filter-outputcache-attribute – amarsuperstar Jul 15 '10 at 13:28
  • I think that is along the same lines however I still don't think it's the same issue. I guess it could be implemented as a filter (stream) though I am not 100% sure how to go about that. – James Hughes Jul 15 '10 at 14:44
  • Excellent this is what I was wanting. On final question though - Why the use of Close vs Flush. I recently found a similar solution but it used Flush and I was wondering which is better and why? – James Hughes Jul 15 '10 at 19:36
  • I'm no expert, but I think Close is safer than Flush, since ASP.NET could flush the response several times if it is large and they want to start sending data to the client. By the time Close is called, atleast by this time it is definite that the output stream has finished. Hope that is vaguely clear – amarsuperstar Jul 16 '10 at 08:36
  • 1
    By replacing all new line characters with an empty string, you run the risk of commenting out everything that follows a js comment line. Unless you remove all comments first. – Daniel Dyson Nov 17 '11 at 13:50
  • 1
    @DanielDyson From the answer "The code below will install a response filter allowing you to manipulate the output, in this case it just removes newline characters". In the context of the question, this was just an example to show how to manipulate the output. The author already knows how to wire up YUI or any other minifier, this was just some boiler plate showing them where to make the call. – amarsuperstar Nov 23 '11 at 13:13
  • 2
    True, however, someone searching for a JS Minifier might come accross this code. You can't assume that only people looking for info on ActionFilters will come accross this answer. So my comment was not aimed at you. Your answer was correct. It was just a warning to others that this is not a working JS minifier. – Daniel Dyson Nov 23 '11 at 15:48