Перехват необработанных исключений в ASP.NET UserControls

StackOverflow https://stackoverflow.com/questions/10793

Вопрос

Я динамически загружаю пользовательские элементы управления, добавляя их в коллекцию элементов управления веб-формы.

Я бы хотел скрыть пользовательские элементы управления, если они вызывают необработанное исключение при рендеринге.

Итак, я попытался подключиться к событию ошибки каждого UserControl, но, похоже, это событие никогда не срабатывает для UserControls, как это происходит для класса Page.

Немного погуглил, и это не кажется многообещающим.Есть какие-нибудь идеи?

Это было полезно?

Решение

mmilic, следующий из ваш ответ к моему предыдущая идея..

Никакой дополнительной логики не требуется!В том-то и дело, что вы ничего не делаете с рассматриваемыми классами, просто заворачиваете их в какую-нибудь пузырчатую пленку для создания экземпляра!:)

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

ПРИНОШУ ИЗВИНЕНИЯ ЗА ДЛИННЫЙ ПОСТ

Загрузчик безопасности

По сути, это и будет тот самый "пузырь", о котором я упоминал..Он получит элементы управления HTML, улавливая любые ошибки, возникающие во время рендеринга.

public class SafeLoader
{
    public static string LoadControl(Control ctl)
    {
        // In terms of what we could do here, its down
        // to you, I will just return some basic HTML saying
        // I screwed up.
        try
        {
            // Get the Controls HTML (which may throw)
            // And store it in our own writer away from the
            // actual Live page.
            StringWriter writer = new StringWriter();
            HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
            ctl.RenderControl(htmlWriter);

            return writer.GetStringBuilder().ToString();
        }
        catch (Exception)
        {
            string ctlType = ctl.GetType().Name;
            return "<span style=\"color: red; font-weight:bold; font-size: smaller;\">" + 
                "Rob + Controls = FAIL (" + 
                ctlType + " rendering failed) Sad face :(</span>";
        }
    }
}

И некоторые элементы управления..

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

Плохой контроль

public class BadControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        throw new ApplicationException("Rob can't program controls");
    }
}

Хороший контроль

public class GoodControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        writer.Write("<b>Holy crap this control works</b>");
    }
}

Страница

Итак, давайте посмотрим на страницу "тест"..Здесь я просто создаю экземпляры элементов управления, беру их html-код и вывожу его, далее я поделюсь мыслями о поддержке дизайнера и т.д..

Скрытый код страницы

    protected void Page_Load(object sender, EventArgs e)
    {
        // Create some controls (BadControl will throw)
        string goodHtml = SafeLoader.LoadControl(new BadControl());
        Response.Write(goodHtml);

        string badHtml = SafeLoader.LoadControl(new GoodControl());
        Response.Write(badHtml);
    }

Мысли

Хорошо, я знаю, о чем вы думаете: "эти элементы управления создаются программно, как насчет поддержки дизайнера?Я потратил чертовы часы на то, чтобы сделать эти элементы управления удобными для дизайнера, а теперь ты портишь мне настроение ".

Итак, я еще толком не тестировал это (вероятно, сделаю через минуту!), Но идея здесь в том, чтобы переопределить метод CreateChildControls для страницы и взять экземпляр каждого элемента управления, добавленного в форму, и запустить его через SafeLoader.Если код пройдет, вы можете добавить его в коллекцию элементов управления как обычно, если нет, то вы можете создать ошибочные литералы или что-то в этом роде, решать вам, мой друг.

Наконец-то..

Опять же, извините за длинный пост, но я хотел получить код здесь, чтобы мы могли обсудить это :) Я надеюсь, это поможет продемонстрировать мою идею :)

Обновить

Протестировано путем добавления элемента управления в конструктор и переопределения метода CreateChildControls с помощью этого, работает нормально, возможно, потребуется некоторая очистка, чтобы все выглядело лучше, но я оставлю это вам ;)

protected override void CreateChildControls()
{
    // Pass each control through the Loader to check
    // its not lame
    foreach (Control ctl in Controls)
    {
        string s = SafeLoader.LoadControl(ctl);
        // If its bad, smack it downnnn!
        if (s == string.Empty)
        {
            ctl.Visible = false; // Prevent Rendering
            string ctlType = ctl.GetType().Name;
            Response.Write("<b>Problem Occurred Rendering " + 
                ctlType + " '" + ctl.ID + "'.</b>");
        }
    }
}

Наслаждайтесь!

Другие советы

Это интересная проблема..Я все еще довольно свеж, когда дело доходит до пользовательских элементов управления и т.д., но вот мои мысли (не стесняйтесь комментировать / поправлять людей!)..(Я как бы думаю / пишу здесь вслух!)

  • Если во время рендеринга, в некоторых случаях, возникает ошибка, не будет ли уже слишком поздно?(поскольку некоторые элементы управления HTML, возможно, уже были отправлены в программу записи и вывода).
  • Следовательно, не было бы лучше обернуть метод рендеринга пользовательского элемента управления, но вместо того, чтобы передавать ему ссылку на "Живой" HtmlTextWriter, вы передаете свой собственный, перехватываете любые исключения, возникающие в этом маленьком "пузыре безопасности", если все пойдет хорошо, вы затем передаете свой результирующий HTML фактическому HtmlTextWriter?
  • Вероятно, эту логику можно было бы перенести в универсальный класс-оболочку, который вы бы использовали для динамической загрузки / рендеринга элементов управления во время выполнения..
  • Если какие-либо ошибки все же возникнут, в вашем распоряжении есть вся необходимая информация!(т.е. контрольные ссылки и т.д.).

Просто мои мысли, вспыхни!:D ;)

В зависимости от того, где возникают ваши ошибки, вы можете сделать что-то вроде...

public abstract class SilentErrorControl : UserControl
{
    protected override void Render( HtmlTextWriter writer )
    {
        //call the base's render method, but with a try catch
        try { base.Render( writer ); }
        catch ( Exception ex ) { /*do nothing*/ }
    }
}

Затем унаследуйте SilentErrorControl вместо UserControl.

Глобальный.asax и Application_Error?

http://www.15seconds.com/issue/030102.htm

Или событие Page_Error только на отдельной Странице:

http://support.microsoft.com/kb/306355

void Page_Load(object sender, System.EventArgs e)
{
    throw(new ArgumentNullException());
}

public void Page_Error(object sender,EventArgs e)
{
    Exception objErr = Server.GetLastError().GetBaseException();
    string err =    "<b>Error Caught in Page_Error event</b><hr><br>" + 
                    "<br><b>Error in: </b>" + Request.Url.ToString() +
                    "<br><b>Error Message: </b>" + objErr.Message.ToString()+
                    "<br><b>Stack Trace:</b><br>" + 
                      objErr.StackTrace.ToString();
    Response.Write(err.ToString());
    Server.ClearError();
}

Кроме того, у Карла Сегина (Привет, Карл!) был пост об использовании HttpHandler вместо:

http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx

(Не уверен, что нужно разрешение на его воспроизведение, но если вы хотите написать ответ, я за ☺)

Как насчет добавления нового подкласса UserControl, который обрабатывает ошибки своих методов визуализации и загрузки (чтобы они скрывались по вашему желанию), а затем наследовали от этого для ваших пользовательских элементов управления?

Я не уверен, что понимаю ваш ответ..Как вы загружаете свои элементы управления и добавляете их в свою коллекцию элементов управления?

В этом и был весь смысл бита, добавленного в разделе "Обновление"..У вас есть возможность гибко использовать Безопасный загрузчик куда вам заблагорассудится.

Я не уверен, почему вы считаете, что у вас нет доступа / контроля над Html?Цель SafeLoader состоит в том, чтобы вы не забота что такое html, вы просто пытаетесь "вывести" элемент управления (внутри "пузыря") и определяете, нормально ли он загружается в своем текущем состоянии.

Если это произойдет (т. е.html возвращается) затем вы можете делать с ним все, что вам нравится, выводить html, добавлять элемент управления в коллекцию controls, что угодно!

Если нет, то опять же, вы можете делать все, что вам нравится, отображать сообщение об ошибке, создавать пользовательское исключение..Выбор за вами!

Я надеюсь, что это поможет вам кое-что прояснить, если нет, то, пожалуйста, крикните :)

Я использовал подход @Keith, но проблема в том, что элемент управления отображается до тех пор, пока не будет сгенерировано исключение, что потенциально приводит к открытым HTML-тегам.Я также отображаю информацию об исключении в элементе управления, если он находится в режиме отладки.

  protected override void Render(System.Web.UI.HtmlTextWriter writer)
  {
     try
     {
        // Render the module to a local a temporary writer so that if an Exception occurs
        // the control is not halfway rendered - "it is all or nothing" proposition
        System.IO.StringWriter sw = new System.IO.StringWriter();
        System.Web.UI.HtmlTextWriter htw = new System.Web.UI.HtmlTextWriter(sw);
        base.Render(htw);

        // We made it!  Copy the Control Render over
        writer.Write(sw.GetStringBuilder().ToString());
     }
     catch (System.Exception ex)
     {
        string message = string.Format("Error Rendering Control {0}\n", ID);
        Log.Error(message, ex);
        if (Page.IsDebug)
           writer.Write(string.Format("{0}<br>Exception:<br><pre>{1}\n{2}</pre>", message, ex.Message, ex.StackTrace));
     }
  }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top