У меня есть блок кода, который реплицируется буквально повсюду. Этот блок кода (около 10 строк) обрабатывает входящие действия, проверяет их, обрабатывает сериализацию JSON и вызывает внутреннюю функцию, подготавливая результат.
Я знаю, что это можно свести к однострочному вызову общей вспомогательной функции, но из-за имплицитов, типов и моего не очень хорошего знания синтаксиса Scala я продолжаю сталкиваться с проблемами.
Код (два отдельных примера, чтобы продемонстрировать, чем они отличаются):
val authenticate = GPAuthenticatedAction(parse.json) { implicit request =>
request.body.validate[AuthenticationRequest] match {
case JsSuccess(request, _) => {
val (status, response) = performAuthentication(request)
status(Json.toJson(response.asInstanceOf[AuthenticationResponse]))
}
case e: JsError => NotAcceptable(Json.toJson(GPError.MalformedJSON.value(e.toString + " REQUEST: " + request.body.toString)))
}
}
val register = GPAuthenticatedAction(parse.json) { implicit request =>
request.body.validate[RegistrationRequest] match {
case JsSuccess(request, _) => {
val (status, response) = performRegistration(request)
status(Json.toJson(response.asInstanceOf[RegistrationResponse]))
}
case e: JsError => NotAcceptable(Json.toJson(GPError.MalformedJSON.value(e.toString + " REQUEST: " + request.body.toString)))
}
}
Как видите, они почти идентичны, за исключением типа запроса (AuthenticationRequest
против RegistrationRequest
) и типа ответа (AuthenticationResponse
против RegistrationResponse
). В противном случае это шаблон.
Должен быть способ перегнать это к чему-то вроде:
val register = GPAuthenticatedAction(parse.son) from(RegistrationRequest, RegistrationResponse)
Я попытался определить from[I,O](request: I, response: O)
, но это привело к множеству проблем (нет информации о том, что JSON десериализует и т. д.). Итак, я попытался немного абстрагироваться, создав пару трейтов, GPRequest
и GPResult
:
trait GPRequest[T] {
implicit val format = Json.format[T]
}
trait GPResponse[T] {
implicit val format: Format[T] = Json.format[T]
def from(error: GPError): GPResponse
}
А затем попытался определить такую функцию, как:
def from[I: GPRequest, O: GPResponse](request: Request) { implicit request =>
request.body.validate[I] match {
case JsSuccess(request, _) => {
val (status, response) = performAuthentication(request)
status(Json.toJson(response.asInstanceOf[O]))
}
case e: JsError => NotAcceptable(Json.toJson(GPError.MalformedJSON.value(e.toString + " REQUEST: " + request.body.toString)))
}
}
Но это приводит ко всем видам проблем. Я вставил ошибки компилятора ниже, но общий смысл таков:
- JSON не может понять, что делать с неявной сериализацией (формат/чтение/запись). Не удается найти
apply
иunapply
фактического типа. - Ошибки параметров типа в GPRequest, GPResponse.
- Не могу объявить запрос как
implicit
(вероятно, мой синтаксис Scala здесь испорчен).
И становится хуже.
Суть вопроса: Есть ли у кого-нибудь чистый шаблон проектирования, который достигает того, что я здесь ищу? (Или, в качестве альтернативы, может ли кто-нибудь, кто знает Play и Scala, дать некоторые рекомендации о том, что делать дальше? ). Это действительно кажется, что это должно быть возможно, но я застрял, выясняя следующий шаг.
Для бесстрашных вот ошибки:
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:33: trait GPRequest takes type parameters
[error] object AuthenticationRequest extends GPRequest {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:121: trait GPResponse takes type parameters
[error] def okResponse(response: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(response.code), instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:121: trait GPResponse takes type parameters
[error] def okResponse(response: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(response.code), instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:109: trait GPResponse takes type parameters
[error] def okResponse(response: Results.Status, instance: GPResponse): (Results.Status, GPResponse) = { (response, instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:109: trait GPResponse takes type parameters
[error] def okResponse(response: Results.Status, instance: GPResponse): (Results.Status, GPResponse) = { (response, instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:37: trait GPResponse takes type parameters
[error] case class AuthenticationResponse(token: Option[GPToken], id: Option[GPID], error: Option[GPError]) extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:98: trait GPResponse takes type parameters
[error] def errorResponse(error: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error.code), instance.from(error.failed.getOrElse(GPError.errorForCode(error.code)))) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:98: trait GPResponse takes type parameters
[error] def errorResponse(error: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error.code), instance.from(error.failed.getOrElse(GPError.errorForCode(error.code)))) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:85: trait GPResponse takes type parameters
[error] def errorResponse(error: GPError, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error), instance.from(error)) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:85: trait GPResponse takes type parameters
[error] def errorResponse(error: GPError, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error), instance.from(error)) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:43: trait GPResponse takes type parameters
[error] object AuthenticationResponse extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:53: trait GPRequest takes type parameters
[error] object RegistrationRequest extends GPRequest {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:57: trait GPResponse takes type parameters
[error] case class RegistrationResponse(token: Option[GPToken], id: Option[GPID], error: Option[GPError]) extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:63: trait GPResponse takes type parameters
[error] object RegistrationResponse extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:73: trait GPRequest takes type parameters
[error] object LogoutRequest extends GPRequest {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:77: trait GPResponse takes type parameters
[error] case class LogoutResponse(error: Option[GPError] = None) extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:81: trait GPResponse takes type parameters
[error] object LogoutResponse extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:88: trait Request takes type parameters
[error] def from[I: GPRequest, O: GPResponse](request: Request) { implicit request =>
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:88: missing parameter type
[error] def from[I: GPRequest, O: GPResponse](request: Request) { implicit request =>
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:91: type mismatch;
[error] found : Any
[error] required: controllers.GPSecurityService.AuthenticationRequest
[error] val (status, response) = performAuthentication(request)
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:92: Any does not take parameters
[error] status(Json.toJson(response.asInstanceOf[O]))
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:15: No unapply or unapplySeq function found
[error] implicit val format = Json.format[T]
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:19: No unapply or unapplySeq function found
[error] implicit val format: Format[T] = Json.format[T]
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:20: trait GPResponse takes type parameters
[error] def from(error: GPError): GPResponse