With a simple wrapper
Since all the methods (at least I assume so) have the same signature, you can put all the redundant code inside a wrapper function which takes the function you need to run as an additional argument. The wrapper will first check for the error and then run the function. As a result, your methods will only need to have relevant code inside, without bothering to check for the error first.
Here's an example, I called the wrapper wrap
just to make it clear:
func (s *apiService) get(request Request) (Result, error) {
//method logic
}
func (s *apiService) put(request Request) (Result, error) {
//method logic
}
func wrap(f func (Request) (Result, error), request Request) (Result, error) {
err := authorizationCheck(request)
if err != nil {
return nil, err
}
return f(request)
}
Then, later in your code:
res, err := wrap(s.get, someRequest)
With a decorator
Very similar to the above, but cleaner: instead of creating a wrapper you can implement a decorator which returns a function which wraps your methods and does the error checking before calling them. This again can only be done if all the methods have the same signature, but it is more powerful and IMHO a cleaner solution than using a wrapper.
Here's an example, the decorator is decorate
(yay to my originality):
func (s *apiService) get(request Request) (Result, error) {
//method logic
}
func (s *apiService) put(request Request) (Result, error) {
//method logic
}
func decorate(f func(Request) (Result, error)) func(Request) (Result, error) {
return func(r Request) (Result, error) {
err := authorizationCheck(r)
if err != nil {
return nil, err
}
return f(r)
}
}
Then, later in your code:
res, err := decorate(s.get)(someRequest)
Methods or plain functions?
Whether you prefer a simple wrapper or a decorator, you can also make them methods of your apiService
object if you want (just by adding (s *apiService)
before their name), there really is no big difference, but this would be the preferred option if you want your struct to have the wrapper/decorator available wherever you use it and not just in that particular file.
The relative calls would then become:
res, err := s.wrap(s.get, someRequest)
and:
res, err := s.decorate(s.get)(someRequest)