开发者

MVC 3 compression filter causing garbled output

开发者 https://www.devze.com 2023-02-02 00:56 出处:网络
So, I have a custom attribute called CompressAttribute which is set up as a global filter in global.asax. It uses reflection to examine the return type of the current action method and if it is \"View

So, I have a custom attribute called CompressAttribute which is set up as a global filter in global.asax. It uses reflection to examine the return type of the current action method and if it is "ViewResult" it compresses the output using ei开发者_如何学Pythonther GZip or Deflate. It works just fine except if a page throws a 500 Server Error. If an error is encountered, instead of displaying the .NET error page, I get a bunch of this:

��������`I�%&/m�{J�J��t��

Apparently it's attempting to encode the 500 Server Error page which is causing problems. What's the best way to handle this?

Here's the filter code:

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


Ok so I was able to resolve this by clearing the Response.Filter property in the Application_Error event:

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

Wondering if there's a more correct way to do it though...


You can also solve this by attaching to OnResultExecuting instead of OnActionExecuting. This gives a few advantages

  1. You can discover the action result without resorting to reflection.
  2. OnResultExecuting won't execute in exceptional cases (MVC will invoke OnException but not OnResultExecuting)

Something like this:

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


The accepted answer won't work if anything has been already written to the output.

Instead of disposing the filter you can make sure the headers are being persisted in place:

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


I had this same problem in asp.net mvc 1.0 browsing for a page that had a RenderAction inside (from the futures assembly). Apparently the problem was that the response was being encoded twice. I had to create an action filter for this child actions so that a flag is set in the DataTokens collection of the RouteData. Then I had to modify the compress filter so that it returned in case the flag was set. I also had to deal with the execution order of the filters. Maybe this can help, verify if the compress filter is being called more than one time when an error page is raised.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号