我正在使用使用 Scala 2.10.2(运行 Java 1.7.0_45)构建的 play 2.2.0。我的应用程序中的大多数路由/端点接收一个 json 请求,使用表单将数据解析为案例类,然后对接收到的数据执行业务逻辑。我已经抽象了这一点。它有效,但我不喜欢的一件事是我将案例类实例转换为 java.lang.Object。然后,特定的处理函数必须将案例类实例重新转换回其原始类型。
我很新玩/ scala。scala中是否有一种更简洁的方法来更改我的处理程序函数上的签名,这样就不需要强制转换了。必须施放然后重新施放感觉是错误的。也许这只是这种情况下抽象的代价(不是双关语)?
下面的代码有:
- 调用管道方法的 RefreshTokenController。注意不必要的演员表。
- 执行不必要强制转换的 ControllerUtil.handleJsonRequest 方法。
我认为我正在寻找的是正确使用 scala 泛型。这不会编译,但类似于:
def handleJsonRequest(form: Form[T], handlerFunction: (T) => SimpleResult, request: Request[Object]): SimpleResult {
在下面的代码中查找“TODO”注释。如果您有一种更好、更“类似游戏”的方式来抽象解析和业务逻辑,则可以加分。例如,我应该有自己的应用程序特定的操作吗?
{"paramA": "paramA_Value", "paramB": "paramB_Value"}
object RefreshTokenController extends Controller {
private case class RequestData(paramA: String, paramB: String)
private val requestForm = Form(
mapping(
"paramA" -> nonEmptyText,
"ParamB" -> nonEmptyText
)(RequestData.apply)(RequestData.unapply)
)
def myEndPoint = Action(ControllerUtil.formOrJsonParser) {
request => {
val response = ControllerUtil.handleJsonRequest(requestForm, requestHandlerFunction, request)
response
}
}
val requestHandlerFunction: (Object) => SimpleResult = processRequest
def processRequest(refreshDataObj: Object) : SimpleResult = {
//TODO: Yuck, how can I get rid of the unnecessary cast
val refreshData: RequestData = refreshDataObj.asInstanceOf[RequestData]
//Business logic removed since it's not relevant
}
}
object ControllerUtil {
/**
* Handles the plumbing of parsing a json request and applying business logic.
*/
def handleAsyncJsonRequest(form: Form[_],
handlerFunction: (Object) => Future[SimpleResult],
request: Request[Object]): Future[SimpleResult] = {
// Convert the request body to JSON even if using URL-Form-Encoding
val formData: Object = parseUsingForm(form, request)
handlerFunction(formData)
}
/**
* Handles the plumbing of parsing a json request and applying business logic.
*
*/
def handleJsonRequest(form: Form[_],
handlerFunction: (Object) => SimpleResult,
request: Request[Object]): SimpleResult = {
//TODO: How can I change the function signature so I can avoid casting/recasting.
val formData: Object = parseUsingForm(form, request)
handlerFunction(formData)
}
def parseUsingForm(form: Form[_], request: Request[Object]): Object = {
val jsonData: JsValue = parseRequestToJson(request)
parseUsingForm(form, jsonData)
}
def parseUsingForm(form: Form[_], jsData: JsValue): Object = {
form.bind(jsData).fold(
formWithErrors => {
val error = Results.BadRequest(formWithErrors.errorsAsJson)
throw new UserErrorException(error)
},
parsedData => {
//TODO: Yuck, I don't like having to cast here.
val formData: Object = parsedData.asInstanceOf[Object]
formData
}
)
}
def parseRequestToJson(request: Request[Object]): JsValue = {
var jsonLoginRequest: JsValue = null
request.body match {
case json: JsObject => jsonLoginRequest = json
case form: Map[String, Seq[String]] => jsonLoginRequest = Json.toJson(form.map {
case (k, v) => (k, v.head)
})
}
jsonLoginRequest
}
/**
* parse html encoded form containing json data or String json data
*/
val formOrJsonParser = parse.using {
request =>
request.contentType.map(_.toLowerCase(Locale.ENGLISH)) match {
case Some("application/json") | Some("text/json") => parse.json
case Some("application/x-www-form-urlencoded") | Some("text/x-www-form-urlencoded") =>
parse.urlFormEncoded
case _ =>
parse.error(Future.successful(Results.UnsupportedMediaType("Invalid content type specified")))
}
}
}