October 2013 - Posts

ASP.NET Bundling and Minification Returning File Not Found (404)
19 October 13 07:19 AM | Scott Mitchell

ASP.NET 4.5 introduced a nifty feature for script and stylesheet bundling and minification, a technique that can drastically reduce the size of your script and stylesheet files and, more importantly, reduce the number of round-trips the browser must make to the server to fully render a web page. Today I was working with a client who had been using bundling and minification with great success for sometime but after a recent site update his script bundle, while being rendered in the web page, was not returning the bundled, minified script content as expected, but rather was returning a 404 error (File Not Found).

I’m going to provide a more in-depth discussion on the issue at hand, but let me state the exact problem and solution in short – the problem was that the name of the script bundle – e.g., ~/bundles/MyBundle – did not match the bundle name referenced in the Scripts.Render statement – e.g., Scripts.Render(“~/bundles/MyBundleTypo”). Unfortunately, this mismatch didn’t produce any sort of runtime error that would alert us to the crux of the problem, but instead “failed silently” and generated a 404 for the requested bundle (“~/bundles/MyBundleTypo”). I was able to narrow down the problem due to the resulting <script> element that was rendered (and generated a 404). Instead of seeing a <script> element like:

<script src="http://nbaweblog.com/bundles/MyBundle?v=aLsVjoQ4OTEtRxZ322JRn0RdnugNXJ-_IdXTAvkYpyU1"></script>

We instead saw one sans the querystring, like so:

<script src="http://nbaweblog.com/bundles/MyBundleTypo"></script>

Using the appropriate bundle name – e.g., Scripts.Render(“~/bundles/MyBundle”) instead of Scripts.Render(“~/bundles/MyBundleTypo”) – fixed the problem.

And now for the more verbose explanation!

Bundling and minification involves two steps:

  1. Defining the script and stylesheet bundles in the BundleConfig.cs class, and
  2. Rendering the appropriate <script> and <style> references in the ASP.NET WebForms master page or ASP.NET MVC layout view.

Step (1) involves creating one or more ScriptBundle or StyleBundle classes and adding them to a BundleCollection. The following snippet shows the bundling added for the ~/bundles/MsAjaxJs bundle (which is included automatically when creating a new ASP.NET WebForms project in Visual Studio):

bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
    "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
    "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
    "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
    "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));

Note the name of the bundle in this instance (~/bundles/MsAjaxJs) and the bundled files: MicorosftAjax.js, MicorosftAjaxApplicationServices.js, and so on.

To render the necessary <script> and <style> references in a web page you add syntax like the following. For an ASP.NET MVC view:

@Scripts.Render("~/bundles/MsAjaxJs")

And for an ASP.NET WebForms web page:

<%: Scripts.Render("~/bundles/MsAjaxJs") %>

When the bundling and minification optimizations are enabled the above markup will generate a <script> element like so:

<script src="http://nbaweblog.com/bundles/MsAjaxJs?v=aLsVjoQ4OTEtRxZ322JRn0RdnugNXJ-_IdXTAvkYpyU1"></script>

Note the querystring, which is used for caching purposes.

My client, however, was getting a different looking <script> element generated, one without the querystring like so:

<script src="http://nbaweblog.com/bundles/MsAjaxJs"></script>

Moreover, the URL, when visited, was returning File Not Found (404), rather than the bundled and minified script content.

The problem, it turned out, was a typo, a transposition of two characters in the Scripts.Render markup. Instead of Scripts.Render(“~/bundles/MsAjaxJs”) my client had inadvertently typed in Scripts.Render(“~/bundles/MsAjxaJs”). Of course, there was no bundle named ~/bundles/MsAjxaJs, but instead of generating a runtime exception (which would have been helpful) we instead just got a 404 for the <script> element.

I actually discovered the typo by using Reflector to scour the code of the System.Web.Optimization assembly in an attempt to ascertain why the rendered <script> element lacked a querystring value. Scripts.Render does not include a querystring under two conditions:

  1. The bundled scripts are registered using an absolute path (e.g., http://www.example.com/js/MyScript.js) rather than a virtual path (e.g., ~/js/MyScript.js), which was not the case here, or
  2. The named bundle could not be found in the bundles table (DING DING DING!!)

And that’s what alerted me to very carefully check to ensure that the bundle names and names specified in Scripts.Render matched precisely, which led me to the typo.

It’s never a comforting sign when you need to dig out Reflector and start parsing through disassembled source code in order to unearth the cause of an error. In my opinion there should be some sort of exception that is raised when requesting a non-existent bundled or minified script from Scripts.Render.

Happy Programming!

Filed under:
Rendering Lists in Irregular Columns Using iText / iTextSharp
16 October 13 01:35 AM | Scott Mitchell

Today’s blog post is going to be a bit obscure, but I stumbled across this problem recently and found a workaround that I thought would be worth sharing for anyone else who found themselves in my place.

iText is an open-source library for programmatically creating PDF documents (iTextSharp is the .NET port of iText). I’ve written before on using iTextSharp to create PDF documents from an ASP.NET page, see:

There’s also an invaluable series of iTextSharp-related posts on Mike Brind’s blog, Create PDFs in ASP.NET.

I recently started work on a project that makes heavy use of iTextSharp to create rather complex PDF documents on the fly. One feature of iText is the ability to lay out text into columns. There are two flavors of columns available:

  • Simple columns – you create simple columns by defining a bounded rectangle, e.g., the lower left and upper right coordinates of the rectangle. The text then flows into the bounds of the column.
  • Irregular columns – with irregular columns you specify an array of coordinates for the left margin(s) and another array of coordinates for the right margin(s). With this approach you can define irregularly shaped columns in order to have the text flow around images and other document elements.

Mike’s Page Layout with Columns post provides a good overview of both simple and irregular columns.

One challenge I bumped into is how iText lays out the elements added to a column. There are two layout modes: composite mode and text mode. To be honest, I am not 100% clear on the fine differences between the two modes, but from my understanding the differences are as follows:

  • Composite mode allows you to add all sorts of elements and the layout defined by those added elements is what dictates the rendered layout. For example, you can add lists and tables paragraphs and other such elements to a column when using composite mode and the layout directives for the lists, tables and paragraphs are respected when the column is rendered.
  • With text mode, on the other hand, you are limited to adding only phrases to the column and any layout directives defined in the phrase(s) are ignored. Consequently, if you add three paragraphs in text mode they all run together, one after another, without space between each paragraph, without indentation, and so on.

Unfortunately, text mode and irregular columns are intertwined, meaning that if you are adding irregular columns you cannot use composite mode for those columns. The downside is that you cannot add lists, tables, or other richer document elements to an irregular column. Additionally, you have to add your own line breaks between paragraphs. For instance, if you examine the code in Mike’s Page Layout with Columns post you’ll see that in his irregular column example he adds a series of Phrases and ends the content of each phrase with two newline characters (\n) to create the whitespace between each paragraph.

So what if you need to add a bulleted or numbered list to an irregular columns, are you out of luck? Well, you’re out of luck if you want to add a List element and have it do the rendering but if you don’t mind doing to rendering yourself you can add your own list-like content. The following code snippet shows how to add both ordered and unordered lists.

First, let’s setup the irregular columns. The following code was lifted directly from Mike’s blog post:

Document doc = new Document();

try
{
    doc.SetMargins(45f, 45f, 60f, 60f);
    Font bodyFont = FontFactory.GetFont("Arial", 12, new BaseColor(0, 0, 0));

    FileStream output = new FileStream(Server.MapPath("~/MyIrregularColumnsExample.pdf"), FileMode.Create);
    PdfWriter writer = PdfWriter.GetInstance(doc, output);
    doc.Open();
    PdfContentByte cb = writer.DirectContent;
    ColumnText ct = new ColumnText(cb);


    float gutter = 15f;
    float colwidth = (doc.Right - doc.Left - gutter) / 2;

    float[] left = { doc.Left + 90f , doc.Top - 80f,
            doc.Left + 90f, doc.Top - 170f,
            doc.Left, doc.Top - 170f,
            doc.Left , doc.Bottom };

    float[] right = { doc.Left + colwidth, doc.Top - 80f,
            doc.Left + colwidth, doc.Bottom };

    ct.SetColumns(left, right);

    ct.Alignment = Element.ALIGN_JUSTIFIED;
    ct.AddText(new Phrase("Lorem ipsum dolor sit amet, ...\n\n", bodyFont));

This code starts be creating a Document and outputting the PDF to a file named MyIrregularColumnsExample.pdf on the file system. Next, the irregular column is defined through a series of left and right coordinates. These coordinates are such that the column will run on the left half of the page and wrap around a rectangle in the upper left corner where an image can be placed. If the intent isn’t clear, the screen shot below should help clarify things; also, you can read Mike’s blog post for details.

Next, the elements for the list are defined as a List<string>:

List<string> items = new List<string>();
items.Add("This is the first item");
items.Add("This is the next item");
items.Add("And here be the last item");

Next we’re ready to add the list! Here’s how you add an ordered list – simply loop through each item in the list and add some space and the appropriate number (1, 2, 3, …).

ct.AddText(new Phrase("Here is an ordered list:\n\n", bodyFont));

for (int i = 0; i < items.Count; i++)
{
    ct.AddText(new Phrase(string.Concat("     ", (i + 1), ".  "), bodyFont));
    ct.AddText(new Phrase(itemsIdea, bodyFont));

    if (i != items.Count - 1)
        ct.AddText(Chunk.NEWLINE);
}
            
ct.AddText(Chunk.NEWLINE);
ct.AddText(Chunk.NEWLINE);

For bulleted lists we can use the unicode character 0x2022 to render the bullet. Or you could choose alternative symbols.

ct.AddText(new Phrase("And here is an unordered list:\n\n", bodyFont));

for (int i = 0; i < items.Count; i++)
{
    ct.AddText(new Phrase(string.Concat("     ", '\u2022', "  "), bodyFont));
    ct.AddText(new Phrase(itemsIdea, bodyFont));

    if (i != items.Count - 1)
        ct.AddText(Chunk.NEWLINE);
}

ct.AddText(Chunk.NEWLINE);
ct.AddText(Chunk.NEWLINE);

That’s all there is to it! Here is a screen shot of the rendered PDF. Note the numbered and bulleted lists in an irregular column.

samplePdf

Happy Programming!

Filed under: ,
More Posts

Archives

My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.