MVC 3压缩过滤器导致输出乱码

时间:2010-12-28 18:06:30

标签: c# asp.net-mvc action-filter

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

`I%&安培/米{JJT

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

以下是过滤器代码:

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

4 个答案:

答案 0 :(得分:21)

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

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

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

答案 1 :(得分:7)

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

  1. 您无需借助反射即可发现动作结果。
  2. OnResultExecuting在特殊情况下不会执行(MVC将调用OnException但不调用OnResultExecuting
  3. 这样的事情:

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

如果已经将任何内容写入输出,则接受的答案将无效。

您可以确保标题持久保存,而不是处理过滤器:

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