how to inject dependencies to a service with MacWi

2020-04-18 06:12发布

问题:

I have a service class, and the service have one method getSomethingFromApi , now , I want to have play Configuration instance so I can pull stuff from the application.conf, and a play WSClient so I can perform http calls.

this is how I want my service to look:

class MyApiService {

  def getSomethingFromApi(whichApi: String): Future[ApiRes] = {
    wsClient.url(configuration.getString(whichApi)).withHttpHeaders(("Content-Type", "application/json")).get.map { res =>
      response.status match {
        case Status.OK => // do something
        case _ => throw new Exception
      }
    }
  }

}

and this is the ServicesModule that is wiring my services:

import com.softwaremill.macwire._

trait ServicesModule {

  lazy val myService: MyApiService = wire[MyApiService]

}

my question now is what is the right way of using wiring play Configuration and WSClient instances..? cause currently i need those instances in my service but i dont have them, how should i do this the right way? thanks

回答1:

With macwire it'll probably look like this

// MyApiService.scala
class MyApiService(wsClient: WSClient) { ... }

// ServicesModule.scala
trait ServicesModule with NingWSComponents {
    lazy val wsClient = wire[WSClient]
    lazy val apiService = wire[MyApiService]
}

I haven't tried using macwire with play myself, so I have relatively low confidence that it'll work on the first try, but macwire play example suggests mixing in certain Play modules to provide values needed for WSClient. Most likely not all of them are needed, but some might be - soo I'd suggest starting with just NingWSComponents and gradually adding more until it works.



回答2:

For the configuration I suggest using something like PureConfig and load the configuration as follows

import pureconfig._
import pureconfig.error.ConfigReaderFailures

case class YourConfClass(name: String, quantity: Int)

val config: Either[pureconfig.error.ConfigReaderFailures,YourConfClass] = loadConfig[YourConfClass]

This then can be passed on to any component of your app using macwire.

As of Play 2.6.X one should use AhcWSComponents that are provided by the ws dependency as follows:

In your build.sbt file add the ws dependency to your project

libraryDependencies += ws

In your module trait mix-in the AhcWSComponents trait and wire the WSClient

trait ServicesModule with AhcWSComponents {
    lazy val wsClient = wire[WSClient]
    lazy val apiService = wire[MyApiService]
}

In your MyApiService add the WSClient as a param. to the constructor

class MyApiService(wsClient: WSClient) { ... }

And now you're done. This general rule applies to all provided dependencies.