3

I brought this up as an aside when I first ran into this problem Previous Question, I couldn't pin it down in my app because there were too many javascript, css and images kicking about that might have been aggravating the problem.

Now I've done a very simple MVC app with no javascipt, no css and no other images and it seems that IE9 calls my Url.Action twice (fiddler confirms) but Chrome and Firefox both do what I expect.

The app is simple, it contains a model that has one property and a method that returns a memorystream (of an MSChart image). The view displays the image and a color picker, when the view is posted to the controller, the controller sets the color for the chart and creates the view. The chart image is displayed by calling a controller RenderChart action via Url.Action, the MemoryStream is passed from the view to the RenderImage action via TempData. This works fine for a GET, but when it's a POST IE9 always requests RenderChart twice and the second time round, TempData has been removed. The problem can be resolved by 'resetting' the TempData in the RenderChart action (commented out line) but that is so hackariffic that it clearly isn't an answer that you'd trust.

I'm not looking for alternatives I already have alternatives but....can anyone explain this behaviour?

Here's the model

   public class ChartModel
    {
        public ChartModel()
        {
            this.ChartColor = Color.Green;
        }
        public ChartModel(Color color)
        {
            this.ChartColor = color;
        }
        public Color ChartColor { get; set; }
        public MemoryStream Chart()
        {
            Chart chart = new Chart();
            chart.Height = 250;
            chart.Width = 450;
            chart.ImageType = ChartImageType.Jpeg;
            chart.RenderType = RenderType.BinaryStreaming;
            chart.BackColor=ChartColor;

            chart.BorderlineDashStyle = ChartDashStyle.Solid;
            chart.BackGradientStyle = GradientStyle.TopBottom;
            chart.BorderlineWidth = 2;
            chart.BorderlineColor = Color.Blue;
            chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
            ChartArea ca = chart.ChartAreas.Add("Default");
            ca.BackColor = Color.Transparent;
            ca.AxisX.IsMarginVisible = false;

            Series series = chart.Series.Add("Browser/Gets");
            series.ChartType = SeriesChartType.Bar;
            string[] browsers = new string[]{"IE9","Chrome","FireFox"};
            int[] gets = new int[]{2,1,1};
            series.Points.DataBindXY(browsers, gets);

            using (MemoryStream memStream = new MemoryStream())
            {
                chart.SaveImage(memStream, ChartImageFormat.Jpeg);
                return memStream;
            }
        }
    }

Here's the view

@model TestChart.Models.ChartModel          
@{
    ViewBag.Title = "Chart";
}
<h2>Chart</h2>
@using (Html.BeginForm("Index", "Chart"))
{
    @Html.DropDownListFor(m => m.ChartColor, new SelectList(Enum.GetNames(typeof(System.Drawing.KnownColor))))
    <br />
    <div>
        <div>
            <br />
            @{TempData["Chart"] = Model.Chart();
            }
            <img alt="Chart" src="@Url.Action("RenderChart", "Chart")" />
        </div>
    </div>
    <input type="submit" value="Post" />
}

And here's the controller

public class ChartController : Controller
    {
        public ActionResult Index( string colorName = "White")
        {
            ChartModel model;
            model = new ChartModel(Color.FromName(colorName));
            return View(model);
        }

        [HttpPost]
        public ActionResult Index(ChartModel model)
        {
            return RedirectToAction("Index", new { colorName = model.ChartColor.Name });
        }

        public FileContentResult RenderChart()
        {
            MemoryStream ms = TempData["Chart"] as MemoryStream;
            // TempData["Chart"] = ms; //uncomment this line to get IE9 to work - odd indeed
            return File(ms.ToArray(), "image/jpeg");
        }
    }

And the resulting HTML looks like...(colors list truncated)

<form action="/Chart" method="post">
<select data-val="true" data-val-required="The ChartColor field is required." id="ChartColor" name="ChartColor"> <option>ActiveBorder</option>
:
<option>MenuHighlight</option>
</select>    <br />
    <div>
        <div>
            <br />
            <img alt="Chart" src="/Chart/RenderChart" />
        </div>
    </div>
    <input type="submit" value="Post" />
</form>
</body>
</html>

And the Fiddler output looks like this Fiddler Fiddler when it works (in compatibility mode) Fiddler working

Fiddler Header - Works OK

GET /Chart/Index/WindowFrame HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:54307/Chart/Index/Menu
Accept-Language: zh,es;q=0.9,en-GB;q=0.7,de-DE;q=0.6,fr-FR;q=0.4,nl;q=0.3,fr-CA;q=0.1
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: localhost:54307
Connection: Keep-Alive
Pragma: no-cache
Cookie: ASP.NET_SessionId=biv2pyhxucudsg3aqsvv3jbs

GET /Chart/RenderChart HTTP/1.1
Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5
Referer: http://localhost:54307/Chart/Index/WindowFrame
Accept-Language: zh,es;q=0.9,en-GB;q=0.7,de-DE;q=0.6,fr-FR;q=0.4,nl;q=0.3,fr-CA;q=0.1
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: localhost:54307
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=biv2pyhxucudsg3aqsvv3jbs

Fiddler Header Failure

GET /Chart/Index/Transparent HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:54307/Chart/Index/WindowFrame
Accept-Language: zh,es;q=0.9,en-GB;q=0.7,de-DE;q=0.6,fr-FR;q=0.4,nl;q=0.3,fr-CA;q=0.1
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: localhost:54307
Connection: Keep-Alive
Pragma: no-cache
Cookie: ASP.NET_SessionId=biv2pyhxucudsg3aqsvv3jbs

GET /Chart/RenderChart HTTP/1.1
Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5
Referer: http://localhost:54307/Chart/Index/Transparent
Accept-Language: zh,es;q=0.9,en-GB;q=0.7,de-DE;q=0.6,fr-FR;q=0.4,nl;q=0.3,fr-CA;q=0.1
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: localhost:54307
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=biv2pyhxucudsg3aqsvv3jbs

And here's some timings for fiddler (post / imageOK / image failed)

Notice that the failed one doesn't start until well after the post is completed. makes me wonder if there isn't some kind of extra thread somewhere that thinks the IMG tag hasn't been fulfilled and decides to go and do it. Mystery indeed.

== FLAGS ==================
BitFlags: [None] 0x0
X-RESPONSEBODYTRANSFERLENGTH: 5627
X-PROCESSINFO: iexplore:3100
X-CLIENTIP: 127.0.0.1
X-HOSTIP: ::1
X-EGRESSPORT: 62803
X-CLIENTPORT: 62801

== TIMING INFO ============
ClientConnected:    21:23:58.866
ClientBeginRequest: 21:23:58.866
ClientDoneRequest:  21:23:58.867
Determine Gateway:  0ms
DNS Lookup:         0ms
TCP/IP Connect: 1ms
HTTPS Handshake:    0ms
ServerConnected:    21:23:58.868
FiddlerBeginRequest:    21:23:58.868
ServerGotRequest:   21:23:58.868
ServerBeginResponse:    21:23:58.928
ServerDoneResponse: 21:23:58.928
ClientBeginResponse:    21:23:58.928
ClientDoneResponse: 21:23:58.928

== FLAGS ==================
BitFlags: [None] 0x0
X-RESPONSEBODYTRANSFERLENGTH: 17612
X-PROCESSINFO: iexplore:3100
X-CLIENTIP: 127.0.0.1
X-HOSTIP: ::1
X-EGRESSPORT: 62804
X-CLIENTPORT: 62802

== TIMING INFO ============
ClientConnected:    21:23:58.866
ClientBeginRequest: 21:23:59.001
ClientDoneRequest:  21:23:59.001
Determine Gateway:  0ms
DNS Lookup:         0ms
TCP/IP Connect: 0ms
HTTPS Handshake:    0ms
ServerConnected:    21:23:59.002
FiddlerBeginRequest:    21:23:59.002
ServerGotRequest:   21:23:59.002
ServerBeginResponse:    21:23:59.012
ServerDoneResponse: 21:23:59.012
ClientBeginResponse:    21:23:59.012
ClientDoneResponse: 21:23:59.012

== FLAGS ==================
BitFlags: [None] 0x0
X-RESPONSEBODYTRANSFERLENGTH: 7996
X-PROCESSINFO: iexplore:3100
X-CLIENTIP: 127.0.0.1
X-HOSTIP: ::1
X-EGRESSPORT: 62807
X-CLIENTPORT: 62805

== TIMING INFO ============
ClientConnected:    21:23:59.062
ClientBeginRequest: 21:23:59.063
ClientDoneRequest:  21:23:59.063
Determine Gateway:  0ms
DNS Lookup:         0ms
TCP/IP Connect: 0ms
HTTPS Handshake:    0ms
ServerConnected:    21:23:59.063
FiddlerBeginRequest:    21:23:59.063
ServerGotRequest:   21:23:59.064
ServerBeginResponse:    21:24:01.597
ServerDoneResponse: 21:24:01.597
ClientBeginResponse:    21:24:01.597
ClientDoneResponse: 21:24:01.598
Community
  • 1
  • 1
K. Bob
  • 2,668
  • 1
  • 18
  • 16
  • What happens if you run the generated HTML through http://validator.w3.org/#validate_by_input? Is the problem occurring in IIS or Cassini? Does the problem go away if you use something like www.yourwebsite.name rather than localhost:61877? Is it running in compatibility mode (http://stackoverflow.com/questions/2742853/force-internet-explorer-8-browser-mode-in-intranet/2745477#2745477)? Can you show us the Fiddler request headers for the 2 requests (successful plus unsuccessful)? – mjwills Nov 17 '11 at 07:00
  • Also, are you running IE9 RTM (http://www.sadev.co.za/content/internet-explorer-9-breaks-localhost)? – mjwills Nov 17 '11 at 07:06
  • @mjwills - HTML validates OK - two warnings, one for HTML5 and one for UTF8 character coding assumed. I don't have anywhere to try it from a proper URL, sorry. Compatablity mode is NOT on, but when I put it on then everything works as normal. IE9 is RTM V.9.0.8113.16421 (9.0.3). Successful Fiddler will be added in a moment. – K. Bob Nov 17 '11 at 11:32
  • What happens if you change the references to JPG to a different file format? I am also interested by the referrer being /Chart/Index/Transparent in the fail case. http://expression.microsoft.com/en-us/dd835379#rendmodemetatags can help you to force compatibility mode. – mjwills Nov 17 '11 at 20:32
  • @mjwills - Changing to PNG made no difference. I'll have a look at the referrer thing more carefully as I normally just pick 'random' colors without paying much attention. – K. Bob Nov 17 '11 at 21:07
  • @mjwills - The referrer seems to be OK. The referrer is the posted page as you'd expect. – K. Bob Nov 17 '11 at 21:11

3 Answers3

4

We had exactly the same problem coincidently using Charts, only difference was ours were custom, but based on MSChart. We now do the following, using a File Content Result instead of an Action Result

Controller:

public FileContentResult GetGraph(int id)
{
    var image = Resolve<CountryModel>().Load(id).Graph; //gets our Bitmap object
    image.Save(HttpContext.Response.OutputStream, ImageFormat.Jpeg);
    var converter = new ImageConverter();

    return new FileContentResult((byte[])converter.ConvertTo(image, typeof(byte[])), "image/jpeg");
}

View:

<img src="@Url.Action("GetGraph", "Country", new {Id = Model.CountryId})" />

Hope this helps

EDIT:

I've just realised you're calling the server in your TempData set!

remove this line from your view:

@{TempData["Chart"] = Model.Chart();

and change your RenderChart to do the following:

public FileContentResult RenderChart()
{         
    return File(new ChartModel().Chart().ToArray(), "image/jpeg");
}

The reason is, when you're actually rendering your view, you're calling the method in the Img tag, but you're also calling it when setting the temp data:

@{TempData["Chart"] = Model.Chart();

Thus invoking the chart method twice. Hope this helps :)

Mathew Thompson
  • 55,877
  • 15
  • 127
  • 148
  • 1
    Good idea (+1) to return the real type but sadly it doesn't resolve the problem, I still get two GET requests to RenderChart and the second one fails. I've edited the question to reflect FileContentResult and added a Fiddler screenshot. – K. Bob Nov 16 '11 at 14:04
  • That solution resolves the problem BUT I already knew that from my previous question. Crucially in this example I don't call `Model.Chart()` twice, it only gets called in the View to populate the `TempData["Chart"]` with a memory stream, in RenderChart the stream is extracted from `TempData["Chart"]` and fed back to the view via FileContentResult. I think the real problem is something wrong with IE9 because Chrome & FireFox and IE8 & IE7 all work as expected. I'm looking for the reason why IE9 is doing it 'wrong' not really looking for a better way to display the image. – K. Bob Nov 16 '11 at 22:26
2

If your view is copy/pasted from when you were having this problem, there is a mismatch of } characters in there.

The reason this is relevant is that the Html.BeginForm is being closed early and will in fact render the closing tag of form element in the wrong place (in the middle of your div). This in turn, creates an invalid HTML situation where the browser needs to determine the best course of action to parse and construct a DOM tree to show you the page.

Internet Explorer (at least older versions and compatibility modes) would in some cases resolve this by duplicating nodes in the DOM. Firefox, Chrome and Safari do things a little smarter/differently.

It could be that the result you are seeing is because IE is creating two <img> elements in its DOM and then requesting the image twice from your script.

This could be confirmed by opening the page in IE9, hitting F12 to open the Developer Tools and then browsing the DOM looking for an extra <img>.

Alex Taylor
  • 1,823
  • 1
  • 13
  • 26
  • The curly braces {} tags are all matched up and the HTML looks OK as well. Nothing looks out of place. Are you missing the one immediately before the TempData[] bit? – K. Bob Nov 12 '11 at 14:46
1

I'm not sure why IE9 is requesting the image twice, but I do have a tip which may help and feel less hackarific:

You are generating the Chart image when the HTML is rendered, rather than when the image is actually requested. If you changed your RenderChart action to accept the colour parameter, and render the image then instead, without using TempData, a number of things would become easier:

  • Your RenderAction method no longer relies on SessionState storage for the TempData image
  • This makes RenderAction easier to test
  • This removes the assumption that the call to /Chart/RenderChart from the img tag will definitely be from the correct page (it's an edge-case threading scenario, sure, but it's possible)
  • Applying a short OutputCache to the RenderAction would prevent multiple images being generated in duplicate for the same colour within the caching period, even if they are requested twice

Since the Url for the image is now more RESTful - i.e. the unique resource identifier identifies the unique resource, rather than relying on a hidden TempData value, you can also change your form to be a GET request, avoiding the "resubmit form" browser alert, or you could even just change the img src via javascript and do away with the form altogether.

Alex Norcliffe
  • 2,439
  • 2
  • 17
  • 21
  • Thanks for taking an interest. From my original issue I resolved the problem following a similar route to your suggestions but this question was specifically to find out if anyone knew why IE9 would execute a GET twice, when Chrome & Firefox do what I'd expect. Interestingly IE8 mode (via IE9) works fine. – K. Bob Nov 15 '11 at 14:28