Mappatura delle eccezioni alla risposta all'errore

5

Immagina un programma che espone un servizio REST / servizio GRPC / qualsiasi servizio, che utilizza diverse librerie di terze parti. Queste librerie possono ovviamente generare eccezioni se qualcosa va storto, ad esempio se l'utente tenta di accedere a qualcosa a cui non è permesso.

Il problema qui è che non è possibile controllare in anticipo se la richiesta è corretta o se l'utente ha abbastanza diritti. Quindi inviamo la richiesta alla libreria di terze parti e riceviamo un'eccezione.

È buona norma cogliere queste eccezioni al livello più alto e associarle a codici di stato come questo?

var statusCode = HttpStatusCode.InternalServerError;
if (ex is ArgumentException || ex is ResourceDoesntExistException)
    statusCode = HttpStatusCode.BadRequest;
else if (ex is UnauthorizedAccessException)
    statusCode = HttpStatusCode.Forbidden;
else if (ex is CustomerNotFoundException)
    statusCode = HttpStatusCode.NotFound;

e quindi restituire il codice di stato con un oggetto di errore:

return new ErrorResponse
{
    Error = new ErrorDescription
    {
        Message = ex.Message,
        Type = ex.GetType().Name
    }
};

Vantaggi che vedo usando questo approccio:

  • Nel programma, non dobbiamo preoccuparci se esponiamo un servizio REST o un servizio SOAP o altro. Basta lanciare un'eccezione, verrà gestita correttamente in seguito.
  • Il chiamante riceve informazioni sufficienti e corrette se qualcosa va storto (a condizione che le eccezioni abbiano nomi e informazioni significativi).
  • La registrazione può anche essere centralizzata. Tutte le eccezioni non gestite verranno registrate nel punto in cui vengono convertite in risposte di errore.

Svantaggi:

  • Sembra un po '"hacky".

Qual è il modo corretto per farlo?

Modifica: dal momento che è stato richiesto, ecco cosa faccio per i servizi gRPC, in cui la mappatura degli errori è abbastanza simile:

private RpcException GenerateRpcException(Exception ex)
{
    var statusCode = StatusCode.Unknown;

    if (ex is ArgumentException || ex is ResourceDoesntExistException)
        statusCode = StatusCode.InvalidArgument;
    else if (ex is UnauthorizedAccessException)
        statusCode = StatusCode.PermissionDenied;
    else if (ex is CustomerNotFoundException)
        statusCode = StatusCode.NotFound;

    var status = new Status(statusCode, ex.Message);
    var exceptionName = ex.GetType().Name;

    var rpcMetadata = new Metadata
    {
        { "exception_name", exceptionName }
    };
    return new RpcException(status, rpcMetadata);
}
    
posta CommonGuy 07.02.2018 - 09:45
fonte

2 risposte

4

Non c'è niente di sbagliato nel normalizzare le eccezioni e gestirle correttamente. La tua classe ErrorResponse sembra essere l'approccio giusto per gestire una chiamata al tuo servizio REST.

Se c'è qualcosa che potrebbe essere migliorato, si tratta di un approccio di gestione degli errori statici. L'elenco delle possibili eccezioni / codici di errore potrebbe facilmente crescere domani e il tuo altrimenti diventerà mostruoso a questo ritmo.

Considera la creazione di una classe ErrorManager che contiene un elenco di interfacce ErrorHandler con due metodi: bool CanHandle(Exception) e ErrorResponse Handle(Exception)

Il tuo ErrorManager quando viene data un'eccezione controllerà stupidamente ogni ErrorHandler registrato con una chiamata a CanHandle con l'eccezione. Quando restituisce true, il controllo si arresta e tu restituisci ErrorHandler dal metodo Handle del gestore. Quindi crei un ErrorHandler per ogni possibile ErrorResponse (e non necessariamente per ogni eccezione).

Questo approccio dinamico ti offre la flessibilità di registrare a livello di codice le classi ErrorHandler o di caricarlo da un database, se lo desideri. Ancora più importante, se decidi di registrare le classi ErrorHandler al momento, dovresti decidere di cambiare il modo in cui carichi questi gestori, ma puoi farlo con un impatto minimo o nullo sul tuo programma così com'è. In sostanza, sei a prova di futuro della gestione degli errori.

Una parola per il saggio: avere un piano per quando non trovi un gestore per un'eccezione. Probabilmente è meglio lasciare un HttpStatusCode.InternalServerError . Mentre potresti creare un gestore che "gestisce sempre" le eccezioni, vorrei sconsigliarlo, dal momento che tecnicamente stiamo parlando di possibili eccezioni conosciute e qualsiasi eccezione che non è prevista è un'eccezione sconosciuta e dovrebbe sicuramente essere contrassegnato in rosso e non semplicemente trattato come qualsiasi altro errore.

    
risposta data 07.02.2018 - 11:45
fonte
1

Mi piace il tuo approccio, tranne il fatto che tradurrei anche ciò che va al cliente nel corpo della risposta all'errore. Come dici tu non hai il controllo sulle librerie di terze parti, inoltre non sai se rilasceranno informazioni sensibili al cliente. Meglio prevenire che curare: registrare l'eccezione nei registri interni e propagare solo le informazioni minime che il client deve conoscere sull'errore.

    
risposta data 07.02.2018 - 23:05
fonte

Leggi altre domande sui tag