Атрибуты FilterAttributes конфликтуют при сжатии, RegexFilter

#asp.net #asp.net-mvc-3 #compression #action-filter

#asp.net #asp.net-mvc-3 #сжатие #действие-фильтр

Вопрос:

У меня есть действие контроллера, подобное этому

 [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Put)]
[InsertScript(Order = 1)]
[Compress(Order = 2)]
public ViewResult Service(Page page) { //omitted }
  

Атрибут InsertScript применил ответ.Фильтр

 response.Filter = new RegexResponseFilter(response.Filter, scriptInsertRegex, replacementString);
  

Фильтр ответов regex основан на записи в блоге здесь. Обратите внимание, я не пытаюсь удалить пробелы, я пытаюсь вставить тег scrip, но это не имеет значения.

Метод записи RegexResponseFilter является таким. Остальное такое же, как в публикации в блоге.

 public override void Write(byte[] buffer, int offset, int count)
{
    // capture the data and convert to string
    var data = new byte[count];
    Buffer.BlockCopy(buffer, offset, data, 0, count);

    // filter the string
    var s = Encoding.Default.GetString(buffer);
    s = regex.Replace(s, replacement);

    // write the data to stream 
    var outdata = Encoding.Default.GetBytes(s);
    response.Write(outdata, 0, outdata.GetLength(0));
}
  

Это отлично работает изолированно. Но когда я применил атрибут сжатия (довольно стандартный, который можно увидеть в любом количестве мест в Интернете), все сломалось, и FF4 выдал сообщение об ошибке.

Страница, которую вы пытаетесь просмотреть, не может быть показана, поскольку она использует недопустимую или неподдерживаемую форму сжатия.

Я почуял отвлекающий маневр и удалил атрибут InsertScript, и это работает. Таким образом, эти два в некотором роде конфликтуют. Вот атрибут сжатия для полноты.

 public class CompressAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //get request and response
        var request = filterContext.HttpContext.Request;
        var response = filterContext.HttpContext.Response;

        //get encoding
        var encoding = request.Headers["Accept-Encoding"];
        if (string.IsNullOrWhiteSpace(encoding))
            return;

        encoding = encoding.ToUpperInvariant();


        if (encoding.Contains("DEFLATE") || encoding == "*")
        {
            response.AppendHeader("Content-encoding", "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
        else if (encoding.Contains("GZIP"))
        {
            response.AppendHeader("Content-encoding", "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }

        base.OnActionExecuting(filterContext);
    }
}
  

Вероятно, это что-то очевидное, но, похоже, это должно сработать. Вставка должна быть применена до того, как произойдет сжатие.

Большое спасибо

Ответ №1:

Попробуйте изменить порядок включения:

 [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Put)]
[Compress(Order = 1)]
[InsertScript(Order = 2)]
public ViewResult Service(Page page) { //omitted }
  

Другая возможность заключается в объединении нескольких подобных фильтров, а затем в использовании одного атрибута action filter:

 response.Filter = new CompressStream(new InsertScript(response.Filter));
  

Чтобы это сработало, вам нужно написать пользовательский CompressStream класс так же, как вы это сделали с InsertScript , а затем иметь один атрибут action filter, который объединит два потока.