| ❗This project is no longer maintained. But there is a Pekko fork at scala-pekko-http-client |
|---|
This is a wrapper around the akka-http-client that adds
- handling for domain errors as HTTP 400 returns
- retry logic
- deadlines
- error handling
- logging
- AWS request signing
libraryDependencies += "io.moia" %% "scala-http-client" % "5.0.0"// create the client
val httpClient = new HttpClient(
config = HttpClientConfig("http", "127.0.0.1", 8888),
name = "TestClient",
httpMetrics = HttpMetrics.none,
retryConfig = RetryConfig.default,
clock = Clock.systemUTC(),
awsRequestSigner = None
)
// make a request
val response: Future[HttpClientResponse] = httpClient.request(
method = HttpMethods.POST,
entity = HttpEntity("Example"),
path = "/test",
headers = Seq.empty,
deadline = Deadline.now + 10.seconds
)
// map the response to your model
response.flatMap {
case HttpClientSuccess(content) => Unmarshal(content).to[MySuccessObject].map(Right(_))
case DomainError(content) => Unmarshal(content).to[DomainErrorObject].map(Left(_))
case failure: HttpClientFailure => throw GatewayException(failure.toString)
}See SimpleExample.scala for a complete example.
The lib outputs the following response objects (see io.moia.scalaHttpClient.HttpClientResponse):
- HTTP 2xx Success =>
HttpClientSuccess - HTTP 3xx Redirect => not implemented yet
- HTTP 400 Bad Request with entity => is mapped to
DomainError⚠️ - HTTP 400 Bad Request without entity =>
HttpClientError - HTTP 4xx, 5xx, others =>
HttpClientError - if the deadline expired =>
DeadlineExpired - if an
AwsRequestSigneris given, but the request already includes an "Authorization" header =>AlreadyAuthorizedException - weird akka-errors =>
ExceptionOccurred
To use a custom logger (for correlation ids etc), you can use the typed LoggingHttpClient.
First create a custom LoggerTakingImplicit:
import com.typesafe.scalalogging._
import org.slf4j.LoggerFactory
object CustomLogging {
final case class LoggingContext(context: String)
implicit val canLogString: CanLog[LoggingContext] = new CanLog[LoggingContext] {
override def logMessage(originalMsg: String, ctx: LoggingContext): String = ???
override def afterLog(ctx: LoggingContext): Unit = ???
}
val theLogger: LoggerTakingImplicit[LoggingContext] = Logger.takingImplicit(LoggerFactory.getLogger(getClass.getName))
}Then create a LoggingHttpClient typed to the LoggingContext:
// create the client
val httpClient = new LoggingHttpClient[LoggingContext](
config = HttpClientConfig("http", "127.0.0.1", 8888),
name = "TestClient",
httpMetrics = HttpMetrics.none[LoggingContext],
retryConfig = RetryConfig.default,
clock = Clock.systemUTC(),
logger = CustomLogging.theLogger,
awsRequestSigner = None
)
// create an implicit logging context
implicit val ctx: LoggingContext = LoggingContext("Logging Context")
// make a request
httpClient.request(HttpMethods.POST, HttpEntity.Empty, "/test", Seq.empty, Deadline.now + 10.seconds)The request function will use the ctx implicitly.
See LoggingExample.scala for a complete example.
To use custom-defined headers, you can extend ModeledCustomHeader from akka.http.scaladsl.model.headers:
import akka.http.scaladsl.model.headers.{ModeledCustomHeader, ModeledCustomHeaderCompanion}
import scala.util.Try
final class CustomHeader(id: String) extends ModeledCustomHeader[CustomHeader] {
override def renderInRequests(): Boolean = true
override def renderInResponses(): Boolean = true
override def companion: ModeledCustomHeaderCompanion[CustomHeader] = CustomHeader
override def value(): String = id
}
object CustomHeader extends ModeledCustomHeaderCompanion[CustomHeader] {
override def name: String = "custom-header"
override def parse(value: String): Try[CustomHeader] = Try(new CustomHeader(value))
}Then simply send them in the request:
val response: Future[HttpClientResponse] = httpClient.request(
method = HttpMethods.POST,
entity = HttpEntity("Example"),
path = "/test",
headers = Seq(new CustomHeader("foobar")),
deadline = Deadline.now + 10.seconds
)Note: If you want to access the headers from the response, you can do so from the data inside the HttpClientSuccess:
case HttpClientSuccess(content) => content.headersSee HeaderExample.scala for a complete example.
Tag the new version (e.g. v5.0.0) and push the tags (git push origin --tags).
You need a public GPG key with your MOIA email and an account on https://oss.sonatype.org that can access the io.moia group.
Add your credentials to ~/.sbt/sonatype_credential and run
sbt:scala-http-client> +publishSignedThen close and release the repository.
sbt:scala-http-client> +sonatypeRelease
Afterwards, add the release to GitHub.