#scala #akka #spray
#scala #akka #распыление
Вопрос:
Я работаю с Spray Akka (я хочу сказать «Фреймворк», но они хотят, чтобы их называли «Библиотекой»). Я получаю вызов REST и возвращаю ответ. Однако я нахожу свою обработку немного повторяющейся. Вот два блока, когда я получил POST
запрос от двух API:
post {
entity(as[JObject]) { company =>
if (AuthInfo.rejectedOrNot) {
val response = (secCompanyActor ? jObjectFromWeb(company))
.mapTo[TransOk]
.map(result => result.succeedOrNot match {
case true => (OK, "transaction successful")
case false => (BadRequest, result.errorMessage)
}).recover { case _ => (BadRequest, "An error has occurred! We will fix this")}
complete(response)
} else {
complete(Unauthorized, "Please use HTTP header to authorize this command.")
}
}
Другой блок поразительно похож, но единственное отличие заключается в том, что вместо отправки jObjectFromWeb(...)
я должен отправить это сообщение: jObjectFromComputer(...)
.
Я хотел создать функцию, в которой я передаю сообщение, а затем отправляю сообщение из этой функции, но сообщения Akka не печатаются! Я понимаю философию нетипизированного сообщения и ни за что не спорю, но мне нужно хорошее решение. Да, одно из решений заключается в том, что я передаю строку типа sendMessageToActor(actor, "jObjectFromWeb")
, а в самой функции я использую соответствие шаблону для отправки соответствующего сообщения.
Но она немного уродлива и не очень модульна (что, если мне нужно отправить 10 сообщений?).
Есть ли какой-либо способ сделать это? Я не совсем знаком с отражением Java, но было бы очень хорошо, если бы я мог просто найти класс / объект обращения к сообщению через строку.
Это моя часть реализации актера Akka:
class SECCompanyActor extends Actor with ActorLogging {
import com.mturk.tasks.SECcompany.SECCompanyProtocol._
def receive = {
case jObjectFromCasper(jObject) =>
//Do some logic
sender ! TransOk(result._1, result._2, result._3)
case jObjectFromWeb(jObject) =>
// Do some other logic
sender ! TransOk(result._1, result._2, result._3)
}
Вот мое сообщение Akka:
object SECCompanyProtocol {
case class jObjectFromCasper(jObject: JObject)
case class TransOk(company: Option[Company.Company], succeedOrNot: Boolean, errorMessage: Option[String])
case class jObjectFromWeb(jObject: JObject)
}
Ответ №1:
Как насчет этого:
def doPost(messageConstructor: Company => AnyRef): Route = {
entity(as[JObject]) { company =>
if (AuthInfo.rejectedOrNot) {
val response = (secCompanyActor ? messageConstructor(company))
.mapTo[TransOk]
.map(result => result.succeedOrNot match {
case true => (OK, "transaction successful")
case false => (BadRequest, result.errorMessage)
}).recover { case _ => (BadRequest, "An error has occurred! We will fix this")}
complete(response)
} else {
complete(Unauthorized, "Please use HTTP header to authorize this command.")
}
}
}
post {
doPost(jObjectFromWeb.apply)
}
Как указано в вашем комментарии, это работает, когда jObjectFromWeb
/ jObjectFromComputer
являются классами case. jObjectFromWeb.apply
соответствует apply
методу сопутствующего объекта класса case, который автоматически создается для классов case. Более типизируемый дизайн наследовал бы оба класса case от одного признака:
trait GenericJObject {
val company: Company
}
case class JObjectFromWeb(override val company: Company) extends GenericJObject
case class JObjectFromComputer(override val company: Company) extends GenericJObject
def doPost(messageConstructor: Company => GenericJObject): Route = {
...
}
Кроме того, для удобства сопровождения и понимания вашего кода было бы лучше соблюдать соглашение о кодировании Scala, где имена классов начинаются с заглавной буквы, иначе кто-то может запутаться и предположить, что jObjectFromXXX
это метод, а не класс case.
Комментарии:
1. Ну, хотя
Akka.ask
может использоваться с методами,jObjectFromWeb
это не метод, аmessage
. Это класс case. Однако ваше предложение может быть более интересным, но я хочу использовать Akka Actor прямо сейчас вместо Akka Future. (Приводятся аргументы, предполагающие, чтоFuture
это лучше, чемActor
)2. Только что отредактировал ее, как я уже говорил в отредактированной версии, я запутался, потому что обычно в Scala методы начинаются со строчной буквы, а классы — с прописной.
3. ДА! Это моя вина. Мне нужно это изменить. Спасибо!
4. Извините! Одна маленькая вещь: я начинаю путаться, потому что я незнаком с используемой вами конструкцией (я понимаю
apply
). Во-первых,doPost()
я все еще отправляюprepareMessage
?5. Я только что исправил опечатку: вызов
ask
должен получатьmessageConstructor(company)
вместоprepareMessage(company)
. Короче говоря, в Scala вы можете использовать любой объект так, как если бы это была функция, определив для негоapply
метод. Итак, когда вы вызываете,JObjectFromWeb(company)
вы на самом деле вызываетеJObjectFromWeb.apply(company)
. При использовании классов case компилятор Scala автоматически определяетapply
метод для своего сопутствующего объекта, который вызывает конструктор класса с теми же параметрами.