#go #go-templates
#Вперед #go-шаблоны
Вопрос:
Если у меня есть структура с подобной функцией:
type data struct{}
func (d *data) Foo() (string, error) {
return "", errors.New("bad")
}
И я вызываю .Foo
шаблон, например:
{{ .Foo }}
Я получаю сообщение об ошибке:
error calling Foo: bad
Это соответствует документации для text/template
:
- Имя ниладического метода данных, которому предшествует точка, например .Method Результатом является значение вызова метода с точкой в качестве получателя, точка.Метод (). Такой метод должен иметь одно возвращаемое значение (любого типа) или два возвращаемых значения, второе из которых является ошибкой. Если у него два и возвращаемая ошибка не равна нулю, выполнение завершается, и ошибка возвращается вызывающей стороне в качестве значения Execute .
Могу ли я определить функцию для «перехвата» этой ошибки и возврата некоторого сообщения по умолчанию вместо остановки выполнения? Например:
func Catch(val string, err error) string {
if err != nil {
return "[render error]"
} else {
return val
}
}
Затем:
map := template.FuncMap{"catch": Catch}
tpl := template.Must(template.New("t").Funcs(map).Parse(`
{{ .Foo | catch }}
`))
b := new(bytes.Buffer)
err := tpl.Execute(b, amp;data{})
Это в настоящее время выдает ошибку — есть ли способ заставить ее работать?
Ответ №1:
Вот мой очень хакерский ответ, который вызывает метод во внутренней структуре, а затем возвращает «отредактировано», если вы возвращаете конкретную ошибку.
// Call calls the given method name on a Message. If the result is
// a PermissionDenied error, return the string "[redacted]" and no error.
//
// This should be used by templates to redact methods. It would be nicer to
// just call the Message directly, but returning an error from a niladic method
// immediately halts template execution, so we need this wrapper around the
// function behavior.
func (r *RedactedMessage) Call(mname string) (interface{}, error) {
if mname == "" {
return nil, errors.New("Call() with empty string")
}
for _, char := range mname {
if !unicode.IsUpper(char) {
return nil, errors.New("Cannot call private method")
}
// only check first character
break
}
t := reflect.ValueOf(r.mv)
m := t.MethodByName(mname)
if !m.IsValid() {
return nil, fmt.Errorf("Invalid method: %s", mname)
}
vals := m.Call([]reflect.Value{})
if len(vals) != 2 {
return nil, fmt.Errorf("Expected to get two values back, got %d", len(vals))
}
if vals[1].IsNil() {
return vals[0].Interface(), nil
}
if reflect.DeepEqual(vals[1].Interface(), config.PermissionDenied) {
return "redacted", nil
}
return nil, vals[1].Interface().(error)
}