Produrre e consumare errori sono aspetti essenziali della gestione degli errori nei servizi basati su Goa. Questa sezione descrive in dettaglio come generare errori all’interno delle tue implementazioni di servizio e come gestire questi errori sul lato client efficacemente.
Goa genera funzioni helper per gli errori definiti, semplificando il processo di
creazione di risposte di errore standardizzate. Queste funzioni helper assicurano che gli errori
siano coerenti e formattati correttamente secondo il design del servizio. Le
funzioni helper sono chiamate Make<NomeErrore>
dove <NomeErrore>
è il nome
dell’errore come definito nel DSL. Inizializzano i campi dell’errore basandosi sul
design del servizio (es. se l’errore è un timeout, temporaneo, ecc.).
Dato il seguente design del servizio:
var _ = Service("divider", func() {
Method("IntegralDivide", func() {
Payload(IntOperands)
Result(Int)
Error("DivByZero", ErrorResult, "Il divisore non può essere zero")
Error("HasRemainder", ErrorResult, "Il resto non è zero")
HTTP(func() {
POST("/divide")
Response(StatusOK)
Response("DivByZero", StatusBadRequest)
Response("HasRemainder", StatusUnprocessableEntity)
})
})
})
var IntOperands = Type("IntOperands", func() {
Attribute("dividend", Int, "Dividendo")
Attribute("divisor", Int, "Divisore")
Required("dividend", "divisor")
})
Esempio di Implementazione:
//...
func (s *dividerSvc) IntegralDivide(ctx context.Context, p *divider.IntOperands) (int, error) {
if p.Divisor == 0 {
return 0, gendivider.MakeDivByZero(fmt.Errorf("il divisore non può essere zero"))
}
if p.Dividend%p.Divisor != 0 {
return 0, gendivider.MakeHasRemainder(fmt.Errorf("il resto è %d", p.Dividend%p.Divisor))
}
return p.Dividend / p.Divisor, nil
}
In questo esempio:
gendivider
è generato da Goa (sotto gen/divider
).MakeDivByZero
crea un errore DivByZero
standardizzato.MakeHasRemainder
crea un errore HasRemainder
standardizzato.Queste funzioni helper gestiscono l’inizializzazione dei campi dell’errore basandosi sul
design del servizio, assicurando che gli errori siano correttamente serializzati e mappati su
codici di stato specifici del trasporto (400 per DivByZero
e 422 per
HasRemainder
in questo esempio).
Per scenari di errore più complessi, potresti aver bisogno di definire tipi di errore personalizzati.
A differenza dell’ErrorResult
predefinito, i tipi di errore personalizzati ti permettono di includere
informazioni contestuali aggiuntive rilevanti per l’errore.
Dato il seguente design del servizio:
var _ = Service("divider", func() {
Method("IntegralDivide", func() {
Payload(IntOperands)
Result(Int)
Error("DivByZero", DivByZero, "Il divisore non può essere zero")
HTTP(func() {
POST("/divide")
Response(StatusOK)
Response("DivByZero", StatusBadRequest)
})
})
})
var DivByZero = Type("DivByZero", func() {
Description("DivByZero è l'errore restituito quando si usa il valore 0 come divisore.")
Field(1, "name", String, "Nome dell'errore", func() {
Meta("struct:error:name")
})
Field(2, "message", String, "Messaggio di errore per la divisione per zero.")
Field(3, "dividend", Int, "Dividendo che è stato usato nell'operazione.")
Required("name", "message", "dividend")
})
Esempio di Implementazione:
func (s *dividerSvc) IntegralDivide(ctx context.Context, p *divider.IntOperands) (int, error) {
if p.Divisor == 0 {
return 0, &gendivider.DivByZero{Name: "DivByZero", Message: "il divisore non può essere zero", Dividend: p.Dividend}
}
// Logica aggiuntiva...
}
In questo esempio:
DivByZero
è un tipo di errore personalizzato definito nel design del servizio.DivByZero
, puoi fornire informazioni di errore dettagliate personalizzate.Meta("struct:error:name")
per permettere a Goa di mappare correttamente
l’errore. Questo attributo deve essere impostato con il nome dell’errore come definito nel
design del servizio.Gestire gli errori sul lato client è importante tanto quanto produrli sul server. Una corretta gestione degli errori assicura che i client possano rispondere appropriatamente a diversi scenari di errore.
Quando si usa il tipo ErrorResult
predefinito, gli errori lato client sono istanze di
goa.ServiceError
. Puoi controllare il tipo di errore e gestirlo basandoti sul
nome dell’errore.
Esempio:
res, err := client.Divide(ctx, payload)
if err != nil {
if serr, ok := err.(*goa.ServiceError); ok {
switch serr.Name {
case "HasRemainder":
// Gestisci l'errore di resto
case "DivByZero":
// Gestisci l'errore di divisione per zero
default:
// Gestisci errori sconosciuti
}
}
}
Quando si usano tipi di errore personalizzati, gli errori lato client sono istanze delle struct Go generate corrispondenti. Puoi fare l’asserzione di tipo dell’errore al tipo personalizzato specifico e gestirlo di conseguenza.
Esempio:
res, err := client.Divide(ctx, payload)
if err != nil {
if dbz, ok := err.(*gendivider.DivByZero); ok {
// Gestisci l'errore di divisione per zero
}
}
Producendo e consumando efficacemente gli errori, assicuri che i tuoi servizi basati su Goa comunichino i fallimenti in modo chiaro e coerente. Utilizzare funzioni helper generate per errori standard e tipi di errore personalizzati per scenari più complessi permette una gestione degli errori flessibile e robusta. Una corretta gestione degli errori lato client migliora ulteriormente l’affidabilità e l’usabilità delle tue API, fornendo feedback significativi agli utenti e permettendo appropriate azioni correttive.