Skip to content

Instantly share code, notes, and snippets.

@tc523
Last active September 6, 2018 09:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tc523/c3b2f6ae026b64c6b2cef0456bc92cde to your computer and use it in GitHub Desktop.
Save tc523/c3b2f6ae026b64c6b2cef0456bc92cde to your computer and use it in GitHub Desktop.
Client-Side HTTP Requests using Scaladsl
package io.microshare.services
import akka.actor.ActorSystem
import akka.http.javadsl.model.headers.AcceptEncoding
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model.Uri.Query
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.{ActorMaterializer, Materializer}
import com.typesafe.scalalogging.StrictLogging
import io.microshare.utils.config.BaseConfig
import spray.json.{DefaultJsonProtocol, JsArray, JsObject, JsValue, JsonParser}
import net.ceedubs.ficus.Ficus._
import java.util.{HashMap, Map}
import akka.util.ByteString
import org.json.JSONObject
import org.json.XML
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.util.parsing.json.JSONObject
class RestfulAPIServices(implicit executionContext: ExecutionContext, actorSystem: ActorSystem, materializer: Materializer)
extends StrictLogging
with SprayJsonSupport
with DefaultJsonProtocol
with BaseConfig
{
//get method with two parameters: url and headers
//url to pass the GET url for any endpoint
//headers map to pass the following:
//authentication/authorization type (BASIC, OAUTH2, API),
//the creds for that specific type of authentication/authroization (username and password for BASIC type, token for OAUTH2 type)
//and/or content type for accept response header
def get(url: String, headers: Map[String, String]): Future[JsObject] = {
val HttpRequest_get = HttpRequest(method = HttpMethods.GET, uri = url, protocol = HttpProtocols.`HTTP/1.1`)
val requestWithAuth: HttpRequest = if (headers.containsKey("authType")) addAuthorization(HttpRequest_get, headers) else HttpRequest_get
val requestWithAcceptResponseHeaderContentType: HttpRequest = if (headers.containsKey("Content-Type")) addAcceptResponseHeaderContentType(requestWithAuth, headers) else requestWithAuth
(for {
response <- Http()(actorSystem).singleRequest(requestWithAcceptResponseHeaderContentType)(materializer)
text <- {
response.status match {
case StatusCodes.Success(_) => {
logger.info(" ***************** response.entity.contentType: " + response.entity.getContentType())
// This call should unmarshal to string from either Json or XML formats... others will probably fail
Unmarshal(response.entity).to[String]
}
case stat: StatusCode => {
throw new Exception(s"GET Fail: $stat, ${response.entity}")
}
}
}
entity <- {
Future(response.entity.getContentType match {
case ContentTypes.`application/json` => {
JsonParser(text.toString).asJsObject
}
case ContentTypes.`text/xml(UTF-8)` => {
JsonParser(XML.toJSONObject(text.toString).toString).asJsObject
}
// These cases attempt to handle specific application defined formats that may be based on XML or JSON
case x if x.toString.contains("xml") => {
logger.info(s"Encountered return Content-type probably containing XML: $x")
JsonParser(XML.toJSONObject(text.toString).toString).asJsObject
}
case x if x.toString.contains("json") => {
logger.info(s"Encountered return Content-type probably containing JSON: $x")
JsonParser(text.toString).asJsObject
}
})
}
} yield {
// logger.debug(s" *************** response: $entity")
entity
}).recover{
case ex => {
logger.error(s"Error occurred during generic REST API GET: ${ex.getMessage}")
throw ex
}
}
} // end get()
//post method with three parameters: url, headers, body
//url to pass the POST url for any endpoint
//body to pass the data being sent with the POST request
//headers map to pass the following:
//authentication/authorization type (BASIC, OAUTH2, API),
//the creds for that specific type of authentication/authroization (username and password for BASIC type, token for OAUTH2 type)
//content type for data/body being sent with the request
def post(url: String, headers: Map[String, String], body: String): Future[JsObject] = {
val data = ByteString(body)
var HttpRequest_post = HttpRequest(method = HttpMethods.POST, uri = url, protocol = HttpProtocols.`HTTP/1.1`)
val requestWithAuth: HttpRequest = if (headers.containsKey("authType")) addAuthorization(HttpRequest_post, headers) else HttpRequest_post
val requestWithContentType: HttpRequest = if (headers.containsKey("Content-Type"))
addContentType(requestWithAuth, data, headers)
else requestWithAuth.withEntity(HttpEntity.Strict(ContentType(MediaTypes.`application/json`), data))
(for {
response <- Http()(actorSystem).singleRequest(requestWithContentType)(materializer)
text <- {
response.status match {
case StatusCodes.Success(_) => {
logger.info(" ***************** response.entity.contentType: " + response.entity.getContentType())
// This call should unmarshal to string from either Json or XML formats... others will probably fail
Unmarshal(response.entity).to[String]
}
case stat: StatusCode => {
throw new Exception(s"POST Fail: $stat, ${response.entity}")
}
}
}
entity <- {
Future(response.entity.getContentType match {
case ContentTypes.`application/json` => {
JsonParser(text.toString).asJsObject
}
case ContentTypes.`text/xml(UTF-8)` => {
JsonParser(XML.toJSONObject(text.toString).toString).asJsObject
}
// These cases attempt to handle specific application defined formats that may be based on XML or JSON
case x if x.toString.contains("xml") => {
logger.info(s"Encountered return Content-type probably containing XML: $x")
JsonParser(XML.toJSONObject(text.toString).toString).asJsObject
}
case x if x.toString.contains("json") => {
logger.info(s"Encountered return Content-type probably containing JSON: $x")
JsonParser(text.toString).asJsObject
}
})
}
} yield {
// logger.debug(s" *************** response: $entity")
entity
}).recover {
case ex => {
logger.error(s"Error occurred during remote microshare post: ${ex.getMessage}")
throw ex
}
}
} // end post()
// This method takes in the http request formed and the authentication/authorization information (the type, and creds) from the headers map
// adds the appropriate header to the http request
// returns the new request with the authorization header added to it
def addAuthorization(request: HttpRequest, authInfo: Map[String, String]): HttpRequest = {
authInfo.get("authType") match {
case "OAUTH2" => request.addHeader(Authorization(OAuth2BearerToken(authInfo.get("token"))))
case "BASIC" => request.addHeader(Authorization(BasicHttpCredentials(authInfo.get("username"), authInfo.get("password"))))
case "API" => request //requires adding apikey token in the url
case _ => request
}
}
// This method is for the GET urls - to accept specific type of response for the GET
// It takes in the http request formed and the content type for the accept response header from the headers map
// adds the appropriate accept response header to the http request
// returns the new request
def addAcceptResponseHeaderContentType(request: HttpRequest, responseHeader: Map[String, String]): HttpRequest = {
responseHeader.get("Content-Type") match {
case "application/json" => request.addHeader(Accept(MediaTypes.`application/json`))
case "application/xml" => request.addHeader(Accept(MediaTypes.`application/xml`))
case "text/plain" => request.addHeader(Accept(MediaTypes.`text/plain`))
case "text/xml" => request.addHeader(Accept(MediaTypes.`text/xml`))
case "text/csv" => request.addHeader(Accept(MediaTypes.`text/csv`))
case "text/html" => request.addHeader(Accept(MediaTypes.`text/html`))
case _ => request
}
}
// This method is for the POST urls - to specify content type of the body/data being sent with the POST
// It takes in the http request formed, the data, and the content type for for the request from the headers map
// add the appropriate entity with content type and data
// returns the new request
def addContentType(request: HttpRequest, data: ByteString, contentTypeInfo: Map[String, String]): HttpRequest = {
contentTypeInfo.get("Content-Type") match {
case "application/json" => request.withEntity(HttpEntity.Strict(ContentType(MediaTypes.`application/json`), data))
case "application/xml" => request.withEntity(HttpEntity.Strict(ContentType.WithCharset(MediaTypes.`application/xml`, HttpCharsets.`UTF-8`), data))
case "text/plain" => request.withEntity(HttpEntity.Strict(ContentType.WithCharset(MediaTypes.`text/plain`, HttpCharsets.`UTF-8`), data))
case "text/xml" => request.withEntity(HttpEntity.Strict(ContentType.WithCharset(MediaTypes.`text/xml`, HttpCharsets.`UTF-8`), data))
case "text/csv" => request.withEntity(HttpEntity.Strict(ContentType.WithCharset(MediaTypes.`text/csv`, HttpCharsets.`UTF-8`), data))
case "text/html" => request.withEntity(HttpEntity.Strict(ContentType.WithCharset(MediaTypes.`text/csv`, HttpCharsets.`UTF-8`), data))
case _ => request.withEntity(HttpEntity.Strict(ContentType(MediaTypes.`application/json`), data))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment