scala – Play Framework:如何实现正确的错误处理
发布时间:2020-12-16 18:20:55 所属栏目:安全 来源:网络整理
导读:我有一个带有几个模块的Play应用程序,每个模块都有自己的异常集.以下是三个例子: 模块常见: package services.commontrait CommonErrors { final case class NotFound(id: String) extends Exception(s"object $id not found") final case class InvalidId
我有一个带有几个模块的Play应用程序,每个模块都有自己的异常集.以下是三个例子:
模块常见: 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) } } 模块认证: 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) } } 模块其他: 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) } } 如您所见,每个特征定义了一组异常,并提供了将该异常转换为JSON响应的方法,如下所示: { "status": 404,"code": "not_found","message": "user 123456789123456789123456 not found","request": "https://myhost.com/users/123456789123456789123456" } 我想要实现的是让每个模块定义它的异常,重用公共模块中定义的异常,并根据需要混合异常特征: 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 } } } 如果你看看我是如何重写toResult的,我总是返回super.toResult,它对应于trait OtherErrors中包含的实现……这个实现可能会遗漏一些预期在CommonErrors.toResult中找到的模式. 我肯定错过了一些东西……所以问题是:用toResult的多个实现解决问题的设计模式是什么? 解决方法
你可以使用
Stackable Trait模式.为简化起见,我会用.getMessage替换你的.toJson:
定义基本特征: trait ErrorsStack { def toResult(e: Exception): Result = e match { case _ => Results.InternalServerError(e.getMessage) } } 和可堆叠的特征: 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) } } 所以,如果我们有一些堆栈 val errors = new CommonErrors with AuthErrors with OtherErrors 并定义了一些帮手 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) } 以下代码 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")) 会产生合理的产量 res0: (Int,String) = (404,user Riddle not found) res1: (Int,String) = (400,user identified by Weasley already exists) res2: (Int,object Gryffindor sword not found) res3: (Int,another error: Snape's death) res4: (Int,String) = (500,Marauders's map) 我们也可以重构这段代码,拉出从特征中归类的案例,并使函数更具组合性: 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 } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |