2016-04-01 32 views
2

我正在使用Playframework 2.5并试图制定一种方法,使所有Json请求/响应控制器方法调用,以避免在每个控制器方法内编写implicit val foo = Json.writes[Bar] or reads[Bar]以及处理基本验证并返回错误代码。如何通过scala中的参数化类型获取隐式val/def?

BaseController将由每个控制器被继承

class BaseController @Inject()(implicit exec: ExecutionContext) extends Controller { 
    def handleJson[T <: RequestModel,U <: ResponseModel](request: Request[JsValue])(cb: (T) => Future[U]):Future[Result] = { 
    implicit val convertReq = Json.reads[T] 
    implicit val convertRes = Json.writes[U] 
    val reqOpt = request.body.asOpt[T] 
    reqOpt match { 
     case Some(data) => 
     cb(data).map{x => Ok(Json.toJson(x))} 
     case None => 
     Future.successful(Ok(Json.obj("foo" -> "bar"))) 
    } 
    } 
} 

控制器

def send = Action.async(parse.json) { req => 
    handleJson[RequestCaseClass,ResponseCaseClass](req)((x) => 
     injectedClass.foo(x.bar).map{ case (success: Boolean,mes: String) => 
     if(success) SendRes(SomethingForSuccess) 
     else SendRes(SomethingForError) 
     } 
    ) 
    } 

代码以上无法编译因为handleJsonimplicit val convertReq = Json.reads[T]implicit val convertRes = Json.writes[U]

否的Json解析器找到的类型T.尝试实现一个implic它 这种类型的读取或格式。

我已经尝试制作案例类的同伴对象,并将其隐含在其中,尽管这并没有解决任何问题。

我的问题是,我如何使用参数化类型的隐式值?

在此先感谢。

回答

3

我建议做这样

class Application @Inject()(implicit exec: ExecutionContext) extends Controller { 
    def handleJson[A : Reads, B : Writes](request: Request[JsValue])(cb: A => Future[B]): Future[Result] = { 
    val reqOpt = request.body.asOpt[A] 
    reqOpt match { 
     case Some(data) => 
     cb(data).map { x => Ok(Json.toJson(x)) } 
     case None => 
     Future.successful(Ok(Json.obj("foo" -> "bar"))) 
    } 
    } 

    def index = Action.async(parse.json) { req => 
    handleJson[MyReq, MyResp](req)(x => Future.successful(MyResp((x.i to x.i + 4).toList))) 
    } 
} 

你不必用任何特征来限制你的模型,只要隐含ReadsWrites就存在。

为确保您的ReadsWrites在班级的伴侣对象中定义,这很好。导入模型类时,您会将它们带入隐式范围。

object MyReq { 
    implicit val myReqReads: Reads[MyReq] = Json.reads[MyReq] 
} 
case class MyReq(i: Int) 

object MyResp { 
    implicit val myRespWrites: Writes[MyResp] = Json.writes[MyResp] 
} 
case class MyResp(l: List[Int]) 
+0

谢谢,这正是我一直在寻找 – suish

3

可以初始化变量implicit之前调用handleJsonconvertRes设置convertReq为隐方法参数handleJson

def send = Action.async(parse.json) { req => 
    implicit val convertReq = Json.reads[RequestCaseClass] 
    implicit val convertRes = Json.writes[ResponseCaseClass] 
    handleJson[RequestCaseClass,ResponseCaseClass](req)((x) => 
     injectedClass.foo(x.bar).map{ case (success: Boolean,mes: String) => 
     if(success) SendRes(SomethingForSuccess) 
     else SendRes(SomethingForError) 
     } 
    ) 
} 

def handleJson[T <: RequestModel,U <: ResponseModel](request: Request[JsValue])(cb: (T) => Future[U])(implicit convertReq: Reads, convertRes: Writes):Future[Result] = { 
    val reqOpt = request.body.asOpt[T] 
    reqOpt match { 
     case Some(data) => 
     cb(data).map{x => Ok(Json.toJson(x))} 
     case None => 
     Future(Ok(Json.obj("foo" -> "bar"))) 
    } 
    } 
+0

我不确定像你这样的多个隐式参数是否(隐式a:T)(隐式b:T)有效或不可行。它应该是'(隐含的a:Reads,b:Writes)'? – suish

+0

与'(隐式a:读取,b:写入)'完美配合。 我仍然需要为每个控制器写入读写操作。 – suish

+1

@suish您可以使用'Json.format [ResponseCaseClass]'来合并写入和读取。在不需要异步时,不要使用'Future.apply',因为它将把新的runnable放到ThreadPool上。最后一行应该是'Future.successful(Ok(Json.obj(...))',这将正确地产生一个'ConstFuture'。 – flavian

相关问题