我有一段代码可以在所有地方复制。这段代码(大约 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
vs RegistrationRequest
)和响应类型(AuthenticationResponse
vs 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)))
}
}
但这会导致各种形式的问题。我在下面粘贴了编译器错误,但一般要点是:
- 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