IE9 неожиданное поведение с MVC, тэгом IMG, Url.Action и TempData - PullRequest
3 голосов
/ 09 ноября 2011

Я поднял этот вопрос в сторону, когда впервые столкнулся с этой проблемой Предыдущий вопрос , я не смог закрепить это в своем приложении, потому что было слишком много javascript, css и изображений, которые могли бы поигратьЯ усугублял проблему.

Теперь я сделал очень простое приложение MVC без javascipt, css и других изображений, и кажется, что IE9 дважды вызывает мой Url.Action (подтверждает fiddler), но Chrome иFirefox делает то, что я ожидаю.

Приложение простое, оно содержит модель с одним свойством и метод, который возвращает поток памяти (изображения MSChart).Представление отображает изображение и палитру цветов. Когда представление публикуется на контроллере, контроллер устанавливает цвет для диаграммы и создает представление.Изображение диаграммы отображается при вызове действия контроллера RenderChart через Url.Action, MemoryStream передается из вида в действие RenderImage через TempData.Это прекрасно работает для GET, но когда это POST, IE9 всегда запрашивает RenderChart дважды и второй раз, TempData удаляется.Проблема может быть решена путем «сброса» TempData в действии RenderChart (закомментированная строка), но это настолько хакерски, что это не тот ответ, которому вы бы доверяли.

IЯ не ищу альтернативы. У меня уже есть альтернативы, но ... может кто-нибудь объяснить такое поведение?

Вот модель

   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;
            }
        }
    }

Вот мнение

@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" />
}

А вот контроллер

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");
        }
    }

И полученный HTML выглядит так ... (список цветов урезан)

<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>

И вывод Fiddler выглядит следующим образомFiddler Fiddler, когда он работает (в режиме совместимости) Fiddler working

Заголовок Fiddler - работает нормально

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

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

И вот некоторыевремя для fiddler (сообщение / imageOK / изображение не удалось)

Обратите внимание, что сбой не начинается до тех пор, пока сообщение не будет завершено.заставляет меня задуматься, не существует ли где-нибудь дополнительного потока, который думает, что тег IMG не был выполнен, и решает пойти и сделать это.Тайна действительно.

== 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

Ответы [ 3 ]

4 голосов
/ 16 ноября 2011

У нас была точно такая же проблема по совпадению с использованием диаграмм, единственная разница была нашей, но была основана на MSChart. Теперь мы делаем следующее, используя Результат содержимого файла вместо результата действия

Контроллер:

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");
}

Вид:

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

Надеюсь, это поможет

EDIT:

Я только что понял, что вы вызываете сервер в вашем наборе TempData!

уберите эту строку из вида:

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

и измените RenderChart, чтобы сделать следующее:

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

Причина в том, что когда вы фактически визуализируете свое представление, вы вызываете метод в теге Img, но вы также вызываете его при установке временных данных :

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

Таким образом, вызывая метод диаграммы дважды. Надеюсь, это поможет:)

2 голосов
/ 12 ноября 2011

Если ваше представление было скопировано / вставлено, когда у вас возникла эта проблема, то там есть несоответствие } символов.

Причина, по которой это важно, заключается в том, что Html.BeginForm закрываетсярано и фактически отобразит закрывающий тег элемента form в неправильном месте (в середине вашего div).Это, в свою очередь, создает недопустимую ситуацию HTML, когда браузер должен определить оптимальный порядок действий для анализа и построения дерева DOM, чтобы показать вам страницу.

Internet Explorer (по крайней мере, более старые версии и режимы совместимости)в некоторых случаях разрешит это путем дублирования узлов в DOM.Firefox, Chrome и Safari делают вещи немного умнее / по-разному.

Возможно, вы видите, что IE создает два элемента <img> в своей DOM, а затем дважды запрашивает изображение из вашего скрипта.

Это можно подтвердить, открыв страницу в IE9, нажав F12, чтобы открыть Инструменты разработчика, а затем просматривая DOM в поисках дополнительного <img>.

.
1 голос
/ 15 ноября 2011

Я не уверен, почему IE9 запрашивает изображение дважды, но у меня есть подсказка, которая может помочь и чувствовать себя менее хакерской:

Вы генерируете изображение диаграммы при рендеринге HTML, а некогда изображение действительно запрашивается.Если вы изменили действие RenderChart так, чтобы оно принимало параметр цвета, и визуализировали изображение, то без использования TempData некоторые вещи стали бы проще:

  • Ваш метод RenderAction больше не использует хранилище SessionState дляизображение TempData
  • Это облегчает тестирование RenderAction
  • Это устраняет предположение, что вызов / Chart / RenderChart из тега img определенно будет с правильной страницы.(это сценарий многопоточности, конечно, но это возможно)
  • Применение короткого OutputCache к RenderAction предотвратит создание нескольких изображений в двух экземплярах для одного цвета в течение периода кэширования, даже если они запрошеныдважды

Поскольку URL для изображения теперь более RESTful - то есть уникальный идентификатор ресурса идентифицирует уникальный ресурс, а не полагаясь на скрытое значение TempData, вы также можете изменить свою форму на GETзапрос, избегая повторной отправки формы«Оповещение браузера, или вы можете просто изменить img src через javascript и полностью отказаться от формы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...