在带有 DI 的 Play 2.4 中,如何在 "Secured" 特征中使用服务 class?

In Play 2.4 with DI, how to use a service class in "Secured" trait?

这是一个 authorisation example from Play Documentation(版本 2.0.4;我试图找到该文档的更新版本但找不到):

trait Secured {

  def username(request: RequestHeader) = request.session.get(Security.username)

  def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Auth.login)

  def withAuth(f: => String => Request[AnyContent] => Result) = {
    Security.Authenticated(username, onUnauthorized) { user =>
      Action(request => f(user)(request))
    }
  }

  def withUser(f: User => Request[AnyContent] => Result) = withAuth { username => implicit request =>
    UserDAO.findOneByUsername(username).map { user =>
      f(user)(request)
    }.getOrElse(onUnauthorized(request))
  }
}

总的来说这很简单,我想用这样的方法。

现在,在 Play 2.4 中,推荐的方法是 不再 使用单例(如上面的 UserDAO),而是 类 和运行时 DI(参见 migration guide, or DI docs).

例如,我的服务和存储库类是这样定义的:

class AuthService @Inject()(accountRepo: AccountRepository) { }

class AccountRepository { }

在使用 Play 2.4 和 DI 的情况下,推荐的/"correct"/获取服务或 DAO 的最简单方法是什么(例如 AuthService 在我的情况下,或 UserDAO 在文档示例中)在 Secured?

这样的特征中

或者你现在是否应该以一种完全不同于使用这种特征的方式来实现对控制器的授权?


我可以按照以下方式让它工作:

trait Secured {
  val authService = GuiceUtils.inject[AuthService]    
  // ...
}

使用这样的助手:

object GuiceUtils {
  lazy val injector = new GuiceApplicationBuilder().injector()    
  def inject[T: ClassTag]: T = injector.instanceOf[T]
}

但根据 中的回答:

In Play you could use the injector directly as long as the Application trait is in scope. But this isn't considered good practice in production code.

如果这是真的,那么在这个用例中认为什么是好的做法?

我认为最简单的方法是在您的特征中声明 authService 但保持抽象,然后让扩展它的控制器处理注入(我相信这就是 MessagesApi/I18nSupport 注入的工作方式)。所以你可以这样做:

trait Secured {
  val authService: AuthService
  ...
}

controller Application @Inject()(override val authService: AuthService) extends Controller with Secured {
  ...
}