Обработка исключений «Maximum request length exceeded» в ASP.NET

в 14:10, , рубрики: .net, ASP.NET, обработка ошибок, ошибки, метки: , ,

На написание данной статьи-заметки меня сподвигла работа на формой обратной связи, в которой имелась возможность отправки файлов на сервер. Естественным образом захотелось ограничить размер загружаемых файлов со стороны сервера и выдавать пользователю соответствующее сообщение. Хорошая новость заключалась в том, что ASP.NET имеет встроенные средства для такого ограничения. Плохая – нет лёгких путей обработки данной ситуации.

Суть проблемы

В ASP.NET можно задать ограничение на размер запроса в web.config:

        <system.web>
            <httpRuntime maxRequestLength="1000"/>

Значение задаётся в килобайтах, по умолчанию – 4096 КБ. Очевидно, что для каждого location можно задавать свои ограничения.

Замечание: В IIS 7+ дополнительно существует своя возможность фильтрации запросов:

<system.webServer>
   <security>
      <requestFiltering>
         <!--ограничение на размер запроса в байтах-->
         <requestLimits maxAllowedContentLength="30000000"></requestLimits>

Т.ч. менять эти два параметра нужно в паре.

Стандартного способа перехвата и обработки исключения о превышении данного ограничения в ASP.NET нет. Первое, что приходит в голову – это ловить исключение в Global.asax обработчиком события Error. Но это оказалось не кошерным по 2-м причинам:

  1. Специального типа исключения нет, а выбрасывается общий System.Web.HttpException (обёрнутое, разумеется, в HttpUnhandledException) с сообщением «Maximum request length exceeded.» в английской локали. Т.ч. гарантированного способа получить нашего «клиента» не имеем.
  2. Опытным путём столкнулся с тем, что к моменту перехвата исключения сервер уже успевает отослать клиенту часть ответа, поэтому осложняется пользование объектом Response.
Решение

Решение подглядел тут. Но, сдаётся мне, что товарищ несколько перемудрил. У меня получилось проще и, на мой взгляд, точнее. В Global.asax в самом начале обработки запроса проверяем его размер и редиректим пользователя, если что на специально подготовленную страничку:

        public override void Init()
        {
            base.Init();
            this.BeginRequest += GlobalBeginRequest;
        }

        private void GlobalBeginRequest(object sender, EventArgs e)
        {
            var runTime = (HttpRuntimeSection)WebConfigurationManager.GetSection("system.web/httpRuntime");
            var maxRequestLength = runTime.MaxRequestLength * 1024;

            if (Request.ContentLength > maxRequestLength)
            {
                // или другой свой код обработки
                Response.Redirect("~/filetoolarge/");
            }
        }

Пара пояснений.

Первое по поводу

            var runTime = (HttpRuntimeSection)WebConfigurationManager.GetSection("system.web/httpRuntime");

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

Второе про

            var maxRequestLength = runTime.MaxRequestLength * 1024;

Я так и не понял, зачем первоисточник пытался вычесть 100 КБ. Ограничение ASP.NET работает на весь запрос в совокупности (тестировалось на ASP.NET 2, .NET 3.5, IIS 6) вместе с данными формы и всеми прикрепленными файлами.

Ну, вот и всё, что я хотел рассказать по этому поводу. Хотя нет. Наверное, нужно добавить, что по-хорошему нужна клиентская валидация, в т.ч. и на размер файла. Для этого нужно использовать сторонние компоненты, т.к. встроенные браузерные контролы загрузки файлов – это отдельный порядочный головняк. Возможно, отдельные реализации умеют решать затронутую проблему комплексно.

Автор: neru

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js