Как определить, какое сообщение об ошибке я должен вернуть из golang API?



У меня есть API GoLang со СПА, чтобы использовать его. Что я делаю с ошибками в моем API, так это возвращаю их до обработчика, где я проверяю, существует ли ошибка из предыдущих функций. Если есть ошибка, я помещаю ее в тело ответа, устанавливаю код состояния на 400 или 500, а затем возвращаю ответ



В функции обработчика, чтобы иметь возможность создать четкое сообщение на стороне клиента, мне нужно знать, какая ошибка была возвращена, как это сделать?



Я знаю о типах ошибок, но я читаю о рекомендации Дэйва Чейни просто вернуть ошибку вместе с сообщением (или обернуть их другими словами).



Но если существует так много видов ошибок, которые могут возникнуть в вызове API, то означает ли это, что перед возвращением ответа мне нужно проверить их все, чтобы знать, какое сообщение я должен вернуть?

551   1  

1 ответ:

Первое, что нужно сказать об ошибках, это то, что просто потому, что есть интерфейс ошибок

type error interface {
    Error() string
}

Не означает, что error, возвращаемый из любого данного метода, может только иметь этот метод / информацию, прикрепленную к нему.

Один из распространенных методов заключается в определении собственного интерфейса ошибок:
type myError interface {
    error // embeds the standard error interface
    OtherMethod() string // can define own methods here
}

При написании методов и функций это действительно важно возвращать error , а не myError, иначе вы связываете этот метод с вашей реализацией ошибки и вызовите себе кошмары зависимости позже.

Теперь, когда мы решили, что можем возвращать дополнительную информацию из error, используя наши собственные интерфейсы ошибок, у вас есть 3 основных варианта.
  1. ошибки часового
  2. типы ошибок отказов
  3. ошибки с поведением

Ошибки часового

Ошибки Sentinel-это значения ошибок, которые определяются как переменные уровня пакета, экспортируются и позволяют сравнивать для проверки ошибок.

package myPackage

var ErrConnectionFailed = errors.New("connection failed")

func Connect() error {
    // trimmed ...
    return ErrConnectionFailed
}

A потребитель этого примера может использовать функцию connect:

if err := myPackage.Connect(); err == myPackage.ErrConnectionFailed {
    // handle connection failed state
}

Вы можете сделать сравнение, чтобы проверить, равна ли возвращенная ошибка ошибке sentinel пакета. Недостатком является то, что любая ошибка, созданная с помощью errors.New("connection failed"), будет равна, а не просто ошибке из myPackage.

Типы ошибок отказов

Несколько лучше, чем ошибки sentinel, являются типы ошибок сбоя. Мы уже видели, что вы можете определить свой собственный интерфейс ошибок, и если мы скажем, что наш теперь:

type MyError interface {
    error
    Failure() string
} 

type Err struct {
    failure string
}

func (e *Err) Error() string {
    // implement standard error
}

func (e *Err) Failure() string {
    return e.failure
}

const ConnFailed = "connection failed"

err := &Err{failure: ConnFailed}

В коде потребителя вы можете получить ошибку, проверить, реализует ли она MyError, а затем сделать с ней что-то.

err := myPackage.Connect()

if myErr, ok := err.(myPackage.MyError); ok {
    // here you know err is a MyError
    if myErr.Failure() == myPackage.ConnFailed {
        // handle connection failed, could also use a switch instead of if
    }
}
Теперь у вас есть представление о том, что вызвало ошибку, которая приятна. Но вас действительно волнует, в чем была причина? Или вас действительно волнует только то, что вы хотите сделать, чтобы справиться с этой ошибкой.

Вот где ошибки с поведением хороши.

Ошибки с поведением

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

package myPackage

// this interface could report if the error
// is temporary and if you could retry it
type tempErr interface {
    Temporary() bool
}

func (e *Err) Temporary() bool {
    // return if the error is temporary or not
}

Теперь в приемнике (обратите внимание, что вам не нужно использовать myPackage.tempErr), вы можете проверить с помощью утверждений типа, если ошибка временная, и обработать случай повторной попытки:

err := myPackage.Connect()

if tmp, ok := err.(interface { Temporary() bool }); ok && tmp.Temporary() {
    // the error is temporary and you can retry the connection
}

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

Если потребитель вашей услуги отправляет вам неверный ввод:

err := doThing(...)

if inv, ok := err.(interface { Invalid() bool }); ok && inv.Invalid() {
    // input is invalid, return 400 bad request status code etc.
}

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

err := doThing(...)

if msg, ok := err.(interface { ResponseMsg() string }); ok {
    // write the message to the http response
    io.WriteString(response, msg.ResponseMsg())
}

TL; DR Вы нужно было бы обрабатывать все случаи, но вы можете создавать типы ошибок, которые делают код намного проще в работе!

Comments

    Ничего не найдено.