È indiscriminatamente in grado di rilevare eccezioni (gestione delle eccezioni Pokemon) sempre accettabile? [duplicare]

14

Normalmente, non prevedo eccezioni e se le ottengo, è qualcosa che non riesco a risolvere nel mio codice: problemi di input dell'utente o problemi di connettività con il database.

Ma gli errori si verificano così, voglio informare il mio utente che qualcosa è andato storto e registrare l'errore.

La mia gestione delle eccezioni è generalmente simile a questa:

public JsonResult Create(UserCreateEditViewModel model)
{
    try
    {
        if (!ModelState.IsValid) return Json(new { IsOk = false, Message = "Some of your data was invalid, please try again." });

        var User = new User
        {
            // map UserCreateEditViewModel properties to User properties
        };

        OrtundEntities Db = new OrtundEntities();
        Db.Users.Add(User);
        Db.SaveChanges();

        return Json
    }
    catch(Exception ex)
    {
        // can't correct user data or repair connection
        // issues to the database so just report the error to the user
        return ReportError(ex, "Create User");
    }
}

public JsonResult ReportError(Exception ex, string action)
{
    return Json(new { IsOk = false, Title = action, Message = ex.Message });
}

Sono consapevole che nella maggior parte dei casi, la gestione delle eccezioni Pokemon è scoraggiato, ma poiché non c'è nulla che io possa fare davvero con un errore, è gestire le eccezioni Pokemon non va bene usare semplicemente per segnalare l'errore?

    
posta Ortund 20.05.2016 - 13:21
fonte

5 risposte

11

Sì. Ogni volta che le eccezioni possono essere esposte agli utenti finali o ai sistemi esterni, potrebbe essere una buona idea cogliere tutte le possibili eccezioni, eseguire una gestione generica (come la registrazione) ed emettere un messaggio generico. Sia per ragioni di usabilità che per ragioni di sicurezza (un'eccezione potrebbe contenere informazioni sensibili).

Ma spesso questo gestore di eccezioni di primo livello non sarebbe implementato come un blocco effettivo di try..catch , ma sarebbe gestito dal framework dell'applicazione. In un tipico framework web non è necessario scrivere try...catch attorno a ogni gestore di richieste o generatore di pagine, ma piuttosto si registrerà il codice generico di gestione delle eccezioni in un unico punto. Se si utilizza ASP.NET MVC, è possibile utilizzare OnException o simili e salvare un gran numero di operazioni di gestione delle eccezioni.

L'importante è che l'applicazione o la richiesta siano ancora terminate . Ciò che è scoraggiato è catturare le eccezioni in modo indiscriminato e quindi continuare l'esecuzione come se nulla fosse accaduto. Poiché un'eccezione imprevista significherebbe che lo stato dell'applicazione è compromesso in qualche modo sconosciuto, l'esecuzione continuata porterà solo a ulteriori errori e corruzioni di dati lungo la linea, il che può essere incredibilmente difficile da eseguire il debug.

Un'altra situazione (meno comune) in cui è opportuno catturare tutto: se si dispone di una libreria non affidabile che fornisce un servizio non essenziale. Supponiamo che tu abbia un'applicazione con un'architettura plug-in in cui terze parti possono fornire moduli che verranno caricati dinamicamente. Avrebbe senso rilevare tutte le eccezioni quando si chiama in questi plug-in, dal momento che non si desidera un plug-in difettoso per interrompere l'intera applicazione.

Anche i moduli interni potrebbero essere considerati inaffidabili. Ad esempio, ho sentito voci secondo cui Microsoft tratta i sottosistemi in Office in questo modo. Se il modulo correttore grammaticale genera un'eccezione imprevista, è preferibile visualizzare una finestra di dialogo con "errore imprevisto nel correttore grammaticale" e continuare senza il controllo, anziché terminare l'intera applicazione.

Si noti che questo funziona solo se il modulo può essere chiaramente isolato dall'applicazione principale, in modo da poter essere sicuri che l'errore all'interno del modulo non corrompa lo stato del programma in generale. Ad esempio, è necessario disabilitare il plug-in o il modulo dopo la prima eccezione imprevista, poiché presenta uno stato danneggiato. Quindi questo è possibile solo se l'applicazione è stata progettata esplicitamente per consentire questo livello di isolamento dei moduli in questione.

    
risposta data 20.05.2016 - 20:34
fonte
3

Pokemon exceptions ... Ever acceptable?

Non dire mai (N) mai, ma ecco qui. Una concettualizzazione concisa delle eccezioni mostra perché.

Un blocco try indica le situazioni che scelgo di non gestire

Dove try , catch e eccezioni di handle after-the-fact sono un problema di progettazione.

I blocchi focalizzati try trasmettono le preoccupazioni del programma. Connessioni inaffidabili, combinazioni di dati utente non definite, percorsi logici specifici che sono vicoli ciechi e così via. Sappiamo tutti che dovremmo racchiudere risorse incontrollabili come una chiamata di DB o file system, ma che devono integrarsi con, essere coerenti con il design.

Certo, non possiamo fare molto dato una connessione DB fallita, ma prendere e / o lanciare e / o rilanciare un'eccezione è ancora una scelta non una necessità assoluta. Inoltre, potrebbero esserci circostanze del dominio aziendale in cui non vogliamo "recuperare".

Sii il più informativo possibile

È tristemente divertente spendere troppe volte il debug delle eccezioni perché non ho idea di quale stato / dati lo abbia causato. Solo far esplodere l'applicazione è essenzialmente la stessa cosa.

Se sai esattamente dove è stato lanciato, o conosci il tipo di eccezione, puoi ottenere dettagli rilevanti da trasmettere. Ad esempio, i valori dei parametri utilizzati in SelectCommand se stiamo parlando di SqlException .

Il tipo di eccezione stesso può essere un'espressione del dominio aziendale dell'applicazione. Potrei creare una o più eccezioni specifiche dell'applicazione che codificano, rinforzano, esprimono vicoli ciechi unici per l'applicazione. Queste classi potrebbero avere costruttori che ci assicurano di acquisire particolari dati utente.

Pokemon è inutile

Rimuovi try/catch dal codice precedente e il comportamento è esattamente uguale. OSSIA provare tutto è come non provare niente.

È stato aggiunto un messaggio "Crea utente" è superfluo perché conosciamo il punto di origine attraverso la traccia dello stack. E il messaggio aggiunge zero contesto per le circostanze specifiche che hanno causato l'eccezione.

Narrow try scope == Context

Se eseguo il wrapping solo della chiamata DB, so esattamente quale (i) eccezione (i) aspettarsi. E so quali dati rilevanti raccogliere per inserire la proprietà Exception.Data .

Narrow catch scope == Context

La "regola del contesto della clausola inversa di cattura" di Bob è una vecchia massima che ho appena inventato. Più in generale si scopre il try (per una buona ragione si assume) quindi i tipi di eccezioni più specifici che potremmo voler catch .

    
risposta data 20.05.2016 - 15:58
fonte
1

Catching Exception di solito è la cosa sbagliata da fare, tuttavia ci sono casi in cui è accettabile e la cosa giusta da fare.

Durante la scrittura di API Web, potresti non voler rivelare troppe informazioni sulle eccezioni ai tuoi clienti. Potresti anche aver progettato nella tua API un indicatore di errore in modo che i clienti possano usarlo per decidere cosa fare dopo. In questa situazione, prendere tutte le eccezioni al limite della tua API può essere la cosa giusta da fare. Certo, dovresti catturare le eccezioni più vicino a dove vengono lanciate, ma a volte le librerie di terze parti non documentano / pubblicano completamente le eccezioni che possono essere generate così quando scrivi un'API devi stare attento. Il limite di un'API può anche essere un luogo eccellente per registrare l'errore perché puoi progettare la tua gestione in modo da avere accesso alla richiesta con cui hai a che fare. Lavorare da una richiesta non riuscita, combinata con un registro delle eccezioni, può essere un modo molto efficace per determinare cosa ha causato un errore.

Ora, guardando il tuo caso particolare, come ho detto nel mio commento, restituire il messaggio di eccezione al client sembra la cosa sbagliata da fare. La maggior parte degli utenti non sarà in grado di interpretare messaggi di eccezione, nel migliore dei casi sarà in grado di segnalare ciò che dice in un rapporto di errore. Tagliare il middle man e registrare l'errore. Restituire invece una sorta di errore generico all'utente.

Inoltre, assicurati che qualsiasi cosa stia chiamando il tuo Create sa cosa fare quando ottiene IsOk==false perché considererà la chiamata un successo piuttosto che un errore a meno che tu non lo dica diversamente.

    
risposta data 20.05.2016 - 17:22
fonte
1

Sì. Per lo meno, è giustificabile dal punto di vista del client per le applicazioni che servono utenti non tecnici, a condizione di dare all'utente istruzioni significative.

Ad esempio, "Si è verificato un errore interno. Ricarica la pagina."

Le eccezioni dovrebbero essere sempre e solo catturate al punto che si possa fare qualcosa di significativo per correggere il problema.

    
risposta data 20.05.2016 - 20:18
fonte
1

No.

Altri hanno notato che potresti voler mostrare agli utenti tutte le eccezioni in una bella finestra di dialogo, o salvarle in un log. Queste non sono idee terribilmente cattive.

Ma cosa succede quando l'eccezione è un StackOverflowException ? Un OutOfMemoryException ? Potresti anche ottenere un ClrException !

In sostanza, semplicemente non puoi catturarli tutti. Puoi provare, e potrebbe anche fornire un certo valore, ma sarà inferiore a quello che ti aspetti: registrare le cose bene sarà probabilmente sufficiente anche senza le eccezioni stesse.

    
risposta data 20.05.2016 - 21:45
fonte

Leggi altre domande sui tag