-
-
Save tc523/c3b2f6ae026b64c6b2cef0456bc92cde to your computer and use it in GitHub Desktop.
Client-Side HTTP Requests using Scaladsl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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