Michael H. Pedersen
Michael H. Pedersen

Reputation: 199

Manually utilize MVC script bundling

Is there a way to programmatically render a script bundle?

Using script bundles in MVC is great. Like it. Use it.

Now I'm writing an application that uses Self-Host WebApi in which I'd like to also create a script bundle. That is, I have a number of javascript files in the project that I wish to minify, combine and then return upon request.

I guess this should be possible. Can't seem to find any info on it, though.

Upvotes: 0

Views: 754

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038710

Unfortunately the ASP.NET bundling mechanism is tightly coupled with an ASP.NET context. Using it in a self host would require some additional work. For example you will need to have a custom virtual path provider which will be able to read files from a specified location:

internal class MyVirtualPathProvider : VirtualPathProvider
{
    private readonly string basePath;
    public MyVirtualPathProvider(string basePath)
    {
        this.basePath = basePath;
    }

    public override bool FileExists(string virtualPath)
    {
        return File.Exists(this.GetFileName(virtualPath));
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        return new MyVirtualFile(this.GetFileName(virtualPath));
    }

    private string GetFileName(string virtualPath)
    {
        return Path.Combine(this.basePath, virtualPath.Replace("~/", ""));
    }

    private class MyVirtualFile : VirtualFile
    {
        private readonly string path;
        public MyVirtualFile(string path)
            : base(path)
        {
            this.path = path;
        }

        public override Stream Open()
        {
            return File.OpenRead(this.path);
        }
    }
}

then you could have a bundle.config file attached to your console application where you would register all your bundle files:

<?xml version="1.0" encoding="utf-8" ?>
<bundles version="1.0">
  <scriptBundle path="~/bundles/myBundle">
    <include path="~/foo.js" />
    <include path="~/bar.js" />
  </scriptBundle>
</bundles>

next you could write your self-host where you would replace the default virtual path provider with the custom one we have just written:

class Program
{
    static void Main()
    {
        var config = new HttpSelfHostConfiguration("http://localhost:8080");
        config.Routes.MapHttpRoute(
            "API Default", 
            "api/{controller}/{id}",
            new { id = RouteParameter.Optional }
        );

        BundleTable.VirtualPathProvider = new MyVirtualPathProvider(Environment.CurrentDirectory);

        using (HttpSelfHostServer server = new HttpSelfHostServer(config))
        {
            server.OpenAsync().Wait();
            Console.WriteLine("Press Enter to quit.");
            Console.ReadLine();
        }
    }
}

and finally you could have some API controller which will serve the custom bundle that we defined in our bundle.config file:

public class MyScriptsController: ApiController
{
    public HttpResponseMessage Get()
    {
        string bundlePath = "~/bundles/myBundle";
        var bundle = BundleTable.Bundles.GetBundleFor(bundlePath);
        var context = new BundleContext(new HttpContext(), BundleTable.Bundles, bundlePath);
        var response = bundle.GenerateBundleResponse(context);
        return new HttpResponseMessage()
        {
            Content = new StringContent(response.Content, Encoding.UTF8, "text/javascript"),
        };
    }

    private class HttpContext : HttpContextBase
    {
    }
}

In this example I have omitted the client side caching that you should obviously take into account inside the controller in order to serve the bundled content with the corresponding Cache headers.

Upvotes: 2

Related Questions