2010-12-28 8 views
17

Tengo un atributo personalizado llamado CompressAttribute que se configura como un filtro global en global.asax. Utiliza la reflexión para examinar el tipo de retorno del método de acción actual y, si es "ViewResult", comprime la salida usando GZip o Deflate. Funciona muy bien, excepto si una página arroja un error de 500 Server. Si se encuentra un error, en lugar de mostrar la página de error de .NET, obtengo un montón de esto:Filtro de compresión MVC 3 que causa una salida confusa

`I % &/m {J J t

Al parecer, está intentando codificar la página 500 Server Error que está causando problemas. ¿Cuál es la mejor manera de manejar esto?

Aquí está el código del filtro:

public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      MethodInfo actionMethodInfo = Common.GetActionMethodInfo(filterContext); 
      if (GetReturnType(actionMethodInfo).ToLower() != "viewresult") return; 

      HttpRequestBase request = filterContext.HttpContext.Request; 

      string acceptEncoding = request.Headers["Accept-Encoding"]; 

      if (string.IsNullOrEmpty(acceptEncoding)) return; 

      acceptEncoding = acceptEncoding.ToUpperInvariant(); 

      HttpResponseBase response = filterContext.HttpContext.Response; 

      if (acceptEncoding.Contains("GZIP")) 
      { 
       response.AppendHeader("Content-encoding", "gzip"); 
       response.Filter = new WebCompressionStream(response.Filter, CompressionType.GZip); 
      } 
      else if (acceptEncoding.Contains("DEFLATE")) 
      { 
       response.AppendHeader("Content-encoding", "deflate"); 
       response.Filter = new WebCompressionStream(response.Filter, CompressionType.Deflate); 
      } 
     } 

Respuesta

21

Ok, así que era capaz de resolver este borrando la propiedad Response.Filter en caso Application_Error:

public void Application_Error(object sender, EventArgs e) 
{ 
    Response.Filter.Dispose(); 
} 

Se pregunta si hay una manera más correcta para hacerlo ...

+0

usted podría intentar crear una página personalizada de error? –

+0

Eso realmente no resolvería el problema. Necesito poder ver información de excepción, información de rastreo de pila, etc. durante la depuración. – Scott

+0

Eso no significa que no puedas hacer eso usando el atributo 'HandleError'. –

1

Tuve este mismo problema en asp.net mvc 1.0 navegando por una página que tenía una RenderAction adentro (del ensamblaje de futuros). Aparentemente, el problema era que la respuesta se codificaba dos veces. Tuve que crear un filtro de acción para estas acciones secundarias para que se establezca un indicador en la colección DataTokens de RouteData. Luego tuve que modificar el filtro de compresión para que volviera en caso de que se configurara el indicador. También tuve que lidiar con el orden de ejecución de los filtros. Tal vez esto pueda ayudar, verificar si el filtro de compresión se está llamando más de una vez cuando se genera una página de error.

6

También puede resolver esto adjuntando a OnResultExecuting en lugar de OnActionExecuting. Esto ofrece algunas ventajas

  1. Puede descubrir el resultado de la acción sin recurrir a la reflexión.
  2. OnResultExecuting no se ejecutará en casos excepcionales (MVC invocará OnException pero no OnResultExecuting)

Algo como esto:

public sealed class MyAttribute : ActionFilterAttribute 
{ 
    /// <summary> 
    /// Called by MVC just before the result (typically a view) is executing. 
    /// </summary> 
    /// <param name="filterContext"></param> 
    public override void OnResultExecuting(ResultExecutingContext filterContext) 
    { 
     var result = filterContext.Result; 
     if (result is ViewResultBase) 
     { 
      var response = filterContext.HttpContext.Response; 

      // Check your request parameters and attach filter. 
     } 
    } 
0

La respuesta aceptada no funcionará si algo ha sido ya escrito a la salida.

En lugar de disponer el filtro puede asegurarse de que las cabeceras están siendo persistieron en su lugar:

protected void Application_PreSendRequestHeaders() 
{ 
    // ensure that if GZip/Deflate Encoding is applied that headers are set 
    // also works when error occurs if filters are still active 
    HttpResponse response = HttpContext.Current.Response; 
    if (response.Filter is GZipStream && response.Headers["Content-encoding"] != "gzip") 
     response.AppendHeader("Content-encoding", "gzip"); 
    else if (response.Filter is DeflateStream && response.Headers["Content-encoding"] != "deflate") 
     response.AppendHeader("Content-encoding", "deflate"); 
} 
Cuestiones relacionadas