I would like to run some code (authorization check) at the start of many functions, such as:
func (s *apiService) get(request Request) (Result, error) {
err := authorizationCheck(request)
if err != nil {
return nil, err
}
//method logic
}
func (s *apiService) put(request Request) (Result, error) {
err := authorizationCheck(request)
if err != nil {
return nil, err
}
//method logic
}
Is there an elegant way to avoid doing the same authorization check at the start of every function?
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)
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)
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With