我正在尝试在Play 2.2中实现REST版本。
我希望客户端在标题中发送以下内容:
Accept: application/vnd.helloworld+json; version=1
根据该版本标题,服务器将调用匹配的控制器操作。我计划为每个版本的api快照一个完整的控制器包。
这样的事情:
com.helloworld.v1.controllers
com.helloworld.v2.controllers
例如:
POST /users/login
{ "email": "foo@gmail.com", "password": "bar" }
我想将该请求指向以下控制器:
com.helloworld.v1.controllers.UserController
我如何干净地在 Global.onRouteRequest 中实现这一目标?
答案 0 :(得分:4)
在考虑了这一段时间之后,我很难想象这种方式按照你描述的方式工作而不使用运行时反射来按版本调用包/类。我也厌倦了这样做,因为你说你的API版本可能有不同的参数,这会使这些类型不兼容。
这是通过在路由中定义默认API URI,然后为每个版本的URI来实现此目的的方法。在onRouteRequest
中,您可以将传入请求重新路由到不同的URI(不使用Redirect
),并且用户仍然不知道存在其他URI。请注意,需要查询参数才能允许它们通过默认网址(如果类型将会发生变化,我不会认为还有其他方式)。
路线:
GET /api controllers.Application.index
GET /v1/api controllers.v1.Test.index(test: Int ?= 0)
GET /v2/api controllers.v2.Test.index(test: String ?= "")
在GlobalSettings
对象中:
override def onRouteRequest(request: RequestHeader): Option[Handler] = {
// Strip the version from the headers
val regex = "version=(\\d+)".r
val version: Option[Int] = request.headers.get("Accept").flatMap(x => regex.findFirstMatchIn(x).map(_.group(1).toInt))
// Copy the version to the URI (if found) in a new RequestHeader (if found), and re-route
val req: RequestHeader = version.map(v =>
request.copy(
uri = "/v" + v.toString + request.uri,
path = "/v" + v.toString + request.path
)
).getOrElse(request)
super.onRouteRequest(req)
}
我认为这个解决方案的唯一部分是不清楚的是枚举路由的必要性,考虑到反向路由编译器通常所做的代码体操,这将难以克服。