j3d*_*j3d 4 error-handling scala playframework
我有一个带有几个模块的Play应用程序,每个模块都有自己的异常集.以下是三个例子:
模块common:
package services.common
trait CommonErrors {
final case class NotFound(id: String) extends Exception(s"object $id not found")
final case class InvalidId(id: String) extends Exception(s"$id is an invalid id")
...
// `toJson` is just an extension method that converts an exception to JSON
def toResult(e: Exception): Result = e match {
case NotFound => Results.NotFound(e.toJson)
case InvalidId => Results.BadRequest(e.toJson)
case _ => Results.InternalError(e.toJson)
}
}
Run Code Online (Sandbox Code Playgroud)
模块auth:
package services.auth
trait AuthErrors {
final case class UserNotFound(e: NotFound) extends Exception(s"user ${e.id} not found")
final case class UserAlreadyExists(email: String) extends Exception(s"user identified by $email already exists")
...
// `toJson` is just an extension method that converts an exception to JSON
def toResult(e: Exception): Result = e match {
case UserNotFound => Results.NotFound(e.toJson)
case UserAlreadyExists => Results.BadRequest(e.toJson)
case _ => Results.InternalError(e.toJson)
}
}
Run Code Online (Sandbox Code Playgroud)
模块other:
trait OtherErrors {
final case class AnotherError(s: String) extends Exception(s"another error: $s")
...
// `toJson` is just an extension method that converts an exception to JSON
def toResult(e: Exception): Result = e match {
case AnotherError => Results.BadRequest(e.toJson)
...
case _ => Results.InternalError(e.toJson)
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,每个特征定义了一组异常,并提供了将该异常转换为JSON响应的方法,如下所示:
{
"status": 404,
"code": "not_found",
"message": "user 123456789123456789123456 not found",
"request": "https://myhost.com/users/123456789123456789123456"
}
Run Code Online (Sandbox Code Playgroud)
我想要实现的是让每个模块定义它的异常,重用模块中定义的异常common,并根据需要混合异常特征:
object Users extends Controller {
val errors = new CommonErrors with AuthErrors with OtherErrors {
// here I have to override `toResult` to make the compiler happy
override def toResult(e: Exception) = super.toResult
}
def find(id: String) = Action { request =>
userService.find(id).map { user =>
Ok(success(user.toJson))
}.recover { case e =>
errors.toResult(e) // this returns the appropriate result
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果你看看我是如何重写的toResult,我总是返回super.toResult,这对应于trait中包含的实现OtherErrors...而且这个实现可能会遗漏一些预期会找到的模式CommonErrors.toResult.
我肯定错过了一些东西......所以问题是:用多种实现来解决问题的设计模式是什么toResult?
Odo*_*ois 11
您可以使用Stackable Trait模式.我会代替你.toJson用.getMessage了简化的原因:
定义基本特征:
trait ErrorsStack {
def toResult(e: Exception): Result = e match {
case _ => Results.InternalServerError(e.getMessage)
}
}
Run Code Online (Sandbox Code Playgroud)
和可堆叠的特征:
trait CommonErrors extends ErrorsStack {
case class NotFound(id: String) extends Exception(s"object $id not found")
case class InvalidId(id: String) extends Exception(s"$id is an invalid id")
override def toResult(e: Exception): Result = e match {
case e: NotFound => Results.NotFound(e.getMessage)
case e: InvalidId => Results.BadRequest(e.getMessage)
case _ => super.toResult(e)
}
}
trait AuthErrors extends ErrorsStack {
case class UserNotFound(id: String) extends Exception(s"user $id not found")
case class UserAlreadyExists(email: String) extends Exception(s"user identified by $email already exists")
override def toResult(e: Exception): Result = e match {
case e: UserNotFound => Results.NotFound(e.getMessage)
case e: UserAlreadyExists => Results.BadRequest(e.getMessage)
case _ => super.toResult(e)
}
}
trait OtherErrors extends ErrorsStack {
case class AnotherError(s: String) extends Exception(s"another error: $s")
override def toResult(e: Exception): Result = e match {
case e: AnotherError => Results.BadRequest(e.getMessage)
case _ => super.toResult(e)
}
}
Run Code Online (Sandbox Code Playgroud)
所以,如果我们有一些堆栈
val errors = new CommonErrors with AuthErrors with OtherErrors
Run Code Online (Sandbox Code Playgroud)
并定义了一些帮手
import java.nio.charset.StandardCharsets.UTF_8
import play.api.libs.iteratee.Iteratee
import concurrent.duration._
import scala.concurrent.Await
def getResult(ex: Exception) = {
val res = errors.toResult(ex)
val body = new String(Await.result(res.body.run(Iteratee.consume()), 5 seconds), UTF_8)
(res.header.status, body)
}
Run Code Online (Sandbox Code Playgroud)
以下代码
import java.security.GeneralSecurityException
getResult(errors.UserNotFound("Riddle"))
getResult(errors.UserAlreadyExists("Weasley"))
getResult(errors.NotFound("Gryffindor sword"))
getResult(errors.AnotherError("Snape's death"))
getResult(new GeneralSecurityException("Marauders's map"))
Run Code Online (Sandbox Code Playgroud)
会产生合理的产量
res0: (Int, String) = (404,user Riddle not found)
res1: (Int, String) = (400,user identified by Weasley already exists)
res2: (Int, String) = (404,object Gryffindor sword not found)
res3: (Int, String) = (400,another error: Snape's death)
res4: (Int, String) = (500,Marauders's map)
Run Code Online (Sandbox Code Playgroud)
我们也可以重构这段代码,拉出从特征中归类的案例,并使函数更具组合性:
type Resolver = PartialFunction[Exception, Result]
object ErrorsStack {
val resolver: Resolver = {
case e => Results.InternalServerError(e.getMessage)
}
}
trait ErrorsStack {
def toResult: Resolver = ErrorsStack.resolver
}
object CommonErrors {
case class NotFound(id: String) extends Exception(s"object $id not found")
case class InvalidId(id: String) extends Exception(s"$id is an invalid id")
val resolver: Resolver = {
case e: NotFound => Results.NotFound(e.getMessage)
case e: InvalidId => Results.BadRequest(e.getMessage)
}
}
trait CommonErrors extends ErrorsStack {
override def toResult = CommonErrors.resolver orElse super.toResult
}
object AuthErrors {
case class UserNotFound(id: String) extends Exception(s"user $id not found")
case class UserAlreadyExists(email: String) extends Exception(s"user identified by $email already exists")
val resolver: Resolver = {
case e: UserNotFound => Results.NotFound(e.getMessage)
case e: UserAlreadyExists => Results.BadRequest(e.getMessage)
}
}
trait AuthErrors extends ErrorsStack {
override def toResult = AuthErrors.resolver orElse super.toResult
}
object OtherErrors {
case class AnotherError(s: String) extends Exception(s"another error: $s")
val resolver: Resolver = {
case e: AnotherError => Results.BadRequest(e.getMessage)
}
}
trait OtherErrors extends ErrorsStack {
override def toResult = OtherErrors.resolver orElse super.toResult
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1235 次 |
| 最近记录: |