c# - update - mvc 4+
Comportamiento inesperado de IE9 con MVC, etiqueta IMG, Url.Action y TempData (3)
No estoy seguro de por qué IE9 está solicitando la imagen dos veces, pero tengo un consejo que puede ayudar y sentir menos hackarific:
Está generando la imagen de gráfico cuando se procesa el HTML, en lugar de cuando se solicita realmente la imagen. Si cambió su acción RenderChart para aceptar el parámetro de color y luego renderizar la imagen, sin usar TempData, varias cosas serían más fáciles:
- Su método RenderAction ya no se basa en el almacenamiento de SessionState para la imagen TempData
- Esto hace que RenderAction sea más fácil de probar
- Esto elimina la suposición de que la llamada a / Chart / RenderChart desde la etiqueta img definitivamente será desde la página correcta (es un escenario de enrutamiento de mayúsculas y minúsculas, seguro, pero es posible)
- La aplicación de un OutputCache corto a RenderAction evitaría que se generen varias imágenes por duplicado para el mismo color dentro del período de almacenamiento en caché, incluso si se solicitan dos veces
Dado que la URL de la imagen ahora es más RESTful, es decir, el único identificador de recursos identifica el recurso único, en lugar de confiar en un valor TempData oculto, también puede cambiar su formulario para que sea una solicitud GET, evitando la alerta de navegador "formulario de reenvío" , o incluso podría simplemente cambiar el img src a través de javascript y eliminar el formulario por completo.
Saqué esto a un lado cuando me topé por primera vez con este problema. Pregunta anterior , no podía identificarlo en mi aplicación porque había demasiados javascript, css e imágenes dando vueltas que podrían haber agravado el problema.
Ahora he hecho una aplicación MVC muy simple sin javascipt, sin CSS y sin otras imágenes, y parece que IE9 llama a mi Url.Action
dos veces (el violinista lo confirma), pero tanto Chrome como Firefox hacen lo que yo esperaba.
La aplicación es simple, contiene un modelo que tiene una propiedad y un método que devuelve un flujo de memoria (de una imagen MSChart). La vista muestra la imagen y un selector de color, cuando la vista se publica en el controlador, el controlador establece el color para el gráfico y crea la vista. La imagen del gráfico se muestra llamando a una acción del controlador RenderChart
través de Url.Action
, el MemoryStream
se pasa de la vista a la acción TempData
través de TempData
. Esto funciona bien para un GET, pero cuando es un POST IE9 siempre solicita RenderChart
dos veces y la segunda vez, TempData
ha sido eliminado. El problema puede resolverse "restableciendo" TempData
en la acción RenderChart
(línea comentada) pero es tan hackariffic que claramente no es una respuesta en la que confíes.
No busco alternativas. Ya tengo alternativas, pero ... ¿alguien puede explicar este comportamiento?
Aquí está el modelo
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;
}
}
}
Aquí está la vista
@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" />
}
Y aquí está el controlador
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");
}
}
Y el HTML resultante parece ... (lista de colores truncada)
<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>
Y la salida de Fiddler se ve así Fiddler cuando funciona (en modo compatibilidad)
Encabezado Fiddler: funciona bien
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
Y aquí hay algunos tiempos para fiddler (post / imageOK / image failed)
Tenga en cuenta que el error no se inicia hasta mucho después de que se complete la publicación. me hace preguntarme si no hay algún tipo de hilo adicional en alguna parte que piense que la etiqueta IMG no se ha cumplido y decide ir y hacerlo. Misterio de hecho
== 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
Si su vista está copiada / pegada desde cuando estaba teniendo este problema, hay una coincidencia de }
caracteres allí.
La razón por la cual esto es relevante es que Html.BeginForm
se cierra antes y, de hecho, representará la etiqueta de cierre del elemento de form
en el lugar equivocado (en el medio de su div
). Esto, a su vez, crea una situación HTML no válida donde el navegador necesita determinar el mejor curso de acción para analizar y construir un árbol DOM para mostrarle la página.
Internet Explorer (al menos versiones anteriores y modos de compatibilidad) en algunos casos resolvería esto al duplicar nodos en el DOM. Firefox, Chrome y Safari hacen las cosas un poco más inteligentes / diferentes.
Podría ser que el resultado que estás viendo se deba a que IE está creando dos elementos <img>
en su DOM y luego solicita la imagen dos veces desde tu script.
Esto podría confirmarse abriendo la página en IE9, presionando F12
para abrir Herramientas de desarrollo y luego navegando en el DOM buscando un <img>
adicional.
Tuvimos exactamente el mismo problema coincidiendo con los Gráficos, solo la diferencia fue la nuestra personalizada, pero basada en MSChart. Ahora hacemos lo siguiente, utilizando un resultado de contenido de archivo en lugar de un resultado de acción
Controlador:
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");
}
Ver:
<img src="@Url.Action("GetGraph", "Country", new {Id = Model.CountryId})" />
Espero que esto ayude
EDITAR:
¡Me acabo de dar cuenta de que está llamando al servidor en su conjunto de TempData!
eliminar esta línea de su vista:
@{TempData["Chart"] = Model.Chart();
y cambie su RenderChart para hacer lo siguiente:
public FileContentResult RenderChart()
{
return File(new ChartModel().Chart().ToArray(), "image/jpeg");
}
La razón es que, cuando estás renderizando tu vista, llamas al método en la etiqueta Img, pero también la llamas cuando configuras los datos temporales :
@{TempData["Chart"] = Model.Chart();
Por lo tanto, invocar el método de gráfico dos veces. Espero que esto ayude :)