Внедрение зависимостей Revel

Я хочу, чтобы мои контроллеры revel использовали различные сервисы, которые я имитирую для модульных тестов. Я новичок в Go; в С# я бы ввел их, используя внедрение зависимостей. Есть ли общий способ сделать это в Revel?

Кажется, лучший способ, который я нашел, - это инициализировать реальные службы в методе Before() контроллера (возможно, используя метод, решаемый проводом), и установить фиктивные версии в методе Before() теста. Или есть лучший способ?


person Stuart Moore    schedule 29.10.2018    source источник
comment
github.com/jwells131313/dargo — это система внедрения зависимостей для go, которая обрабатывает добавление тестовых макетов, которые переопределяют настоящий сервис для модульного тестирования. Это может быть то, что вы ищете   -  person jwells131313    schedule 29.10.2018
comment
Спасибо. Я борюсь с тем, как соединить это с Revel, т.е. где правильно разместить код, который получает зависимости, чтобы Revel выполнял его как часть создания конструктора.   -  person Stuart Moore    schedule 29.10.2018
comment
К сожалению, я не могу помочь с этим, я ничего не знаю о revel. Dargo позволяет использовать функции конструктора для сервисов, и вы могли бы использовать эту функцию конструктора для создания своих сервисов Revel?   -  person jwells131313    schedule 29.10.2018


Ответы (2)


Я использую фильтр для внедрения зависимостей.

Фильтр проверяет, реализует ли контроллер определенные интерфейсы, и вставляет правильную зависимость. Вот пример, который вставляет зависимость, связанную с базой данных:

func DependencyFilter(c *revel.Controller, filterChain []revel.Filter) {
    if ctrl, ok := c.AppController.(DataServiceController); ok {
        ctrl.SetDataService(<your dependency>)
    }

    // Different dependencies could be injected here:
    //if ctrl, ok := c.AppController.(FooController); ok {
    //  ctrl.SetFooService(<your dependency>)
    //}

    // Call the next filter
    if len(filterChain) > 0 {
        filterChain[0](c, filterChain[1:])
    }
}

Где DataServiceController:

type DataServiceController interface {
    SetDataService(ds services.DataService)
}

Я вставил свой фильтр предпоследней записью в init.go:

revel.Filters = []revel.Filter{
    revel.PanicFilter,             // Recover from panics and display an error page instead.
    // ...
    DependencyFilter,              // Add dependencies
    revel.ActionInvoker,           // Invoke the action.
}

Большинству моих контроллеров нужны одни и те же зависимости, поэтому у меня есть базовый контроллер, который они все встраивают:

type BaseController struct {
    *revel.Controller
    DataService services.DataService
}

func (c *BaseController) SetDataService(ds services.DataService) {
    c.DataService = ds
} 

Итак, мои конкретные контроллеры выглядят так:

type Home struct {
    BaseController
}

func (c Home) Index() revel.Result {
    // ...
}

Возможно, есть лучшие способы, но это мой подход.

person Duncan Jones    schedule 24.03.2019

На самом деле существует множество систем DI для GO. Я искал несколько, пытался использовать и, наконец, выбрал один с некоторыми патчами для большего удобства. Использование довольно простое:

package dependency

import (
   "fmt"

   "github.com/lisitsky/inject"
)

func init() {
   inject.Provide(NewStringer)
}

type stringer struct{}

func (s stringer) String() string {
    return "Hello, World"
}

func NewStringer() fmt.Stringer {
    return stringer{}
}

На стороне, принимающей зависимость (main.go):

package main


import (
   "fmt"

   "github.com/lisitsky/inject"

   _ "github.com/lisitsky/inject/examples/simple/dependency"
)

var ( 
   str fmt.Stringer
)

func main() {
   inject.Construct(&str)
   fmt.Println("My Stringer is:", str)
}

Также он поддерживает отложенную инициализацию:

func main() {
   // define variables to be constructed later
   inject.ConstructLater(&str)

   // define dependency providers
   inject.Provide(NewStringer)

   // finalize construction - all DI variables would be initialized at one call
   injector.FinishConstruct()

   fmt.Println("My Stringer is:", str)
}
person Eugene Lisitsky    schedule 31.10.2018
comment
Спасибо - я специально ищу предлагаемый способ использования DI с Revel. Кажется, его нет - лучшее, что я придумал, - это получить зависимости от метода Before() каждого контроллера, который работает, но может привести к ненужной работе, поскольку это происходит для каждого запроса, а не один раз для каждого контроллера. - person Stuart Moore; 01.11.2018