2

我有一段代码可以在所有地方复制。这段代码(大约 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)))
    }
}

正如你所看到的——除了请求类型(AuthenticationRequestvs RegistrationRequest)和响应类型(AuthenticationResponsevs RegistrationResponse)之外,非常非常几乎相同。否则它是样板文件。

应该有一种方法可以将其提炼成以下内容:

val register = GPAuthenticatedAction(parse.son) from(RegistrationRequest, RegistrationResponse)

我尝试定义 a 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)))
    }
}

但这会导致各种形式的问题。我在下面粘贴了编译器错误,但一般要点是:

  1. JSON 无法弄清楚如何处理隐式序列化(格式/读取/写入)。找不到applyunapply的实际类型。
  2. GPRequest、GPResponse 上的类型参数错误。
  3. 无法将请求声明为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
4

1 回答 1

1

您可以编写父案例类而不是您的特征并扩展您现有的类型:

AuthenticationRequest, RegistrationRequest, AuthenticationResponse, RegistrationResponse

作为您的新父案例类的子类。

有了这个,您可以更改您的签名以扩展您的泛型类型,如下所示:

def from[I <: GPRequest, O <: GPResponse]
于 2015-06-11T19:30:16.847 回答