MVC 3压缩filter导致输出乱码

所以,我有一个名为CompressAttribute的自定义属性,它被设置为global.asax中的全局filter。 它使用reflection来检查当前操作方法的返回类型,如果它是“ViewResult”,它使用GZip或Deflate压缩输出。 它工作得很好,除非页面抛出500服务器错误。 如果遇到错误,而不是显示.NET错误页面,我得到一堆:

`我%/米{JJT

显然它正在尝试编码导致问题的500 Server Error页面。 处理这个问题的最佳方法是什么?

这是filter代码:

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

好的,我可以通过清除Application_Error事件中的Response.Filter属性来解决这个问题:

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

想知道是否有更正确的方法来做到这一点……

您也可以通过附加到OnResultExecuting而不是OnActionExecuting来解决此问题。 这提供了一些优点

  1. 您可以在不借助reflection的情况下发现动作结果。
  2. 在exception情况下, OnResultExecuting将不会执行(MVC将调用OnException而不是OnResultExecuting

像这样的东西:

 public sealed class MyAttribute : ActionFilterAttribute { ///  /// Called by MVC just before the result (typically a view) is executing. ///  ///  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. } } 

我在asp.net mvc 1.0中浏览过一个内部有RenderAction的页面(来自期货assembly)也有同样的问题。 显然问题是响应被编码了两次。 我必须为这个子操作创建一个动作filter,以便在RouteData的DataTokens集合中设置一个标志。 然后我不得不修改压缩filter,以便在设置标志的情况下返回。 我还必须处理filter的执行顺序。 也许这可以帮助,validation在引发错误页面时是否多次调用压缩filter。

如果已经将任何内容写入输出,则接受的答案将不起作用。

您可以确保标头持久保存,而不是处理filter:

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