编译错误:找不到实例:转换[models.Errorcode,_ <: Product] on Scala 3 Playframework

问题描述 投票:0回答:1

我最近将一个 playframework 项目从 scala2 迁移到 scala3,我发现这个错误只发生在 scala 3 中,但不会发生在 scala 2 中:

play.sbt.PlayExceptions$CompilationException: Compilation error[Instance not found: 'Conversion[models.ErrorCode, _ <: Product]']
        at play.sbt.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:28)
        at play.sbt.PlayExceptions$CompilationException$.apply(PlayExceptions.scala:28)
        at scala.Option.map(Option.scala:230)
        at play.sbt.run.PlayReload$.$anonfun$taskFailureHandler$8(PlayReload.scala:119)
        at scala.Option.map(Option.scala:230)
        at play.sbt.run.PlayReload$.taskFailureHandler(PlayReload.scala:92)
        at play.sbt.run.PlayReload$.$anonfun$compile$3(PlayReload.scala:144)
        at scala.util.Either$LeftProjection.map(Either.scala:573)
        at play.sbt.run.PlayReload$.compile(PlayReload.scala:144)
        at sbt.PlayRun$.$anonfun$playRunTask$5(PlayRun.scala:99)

导致错误的行是:

implicit val formatError: OFormat[ErrorCode] = Json.format[ErrorCode]
implicit val formatAccountException: OFormat[AccountException] = Json.format[AccountException]

由于它是一个实例未找到异常,我假设我需要在代码的某个地方使用 new ErrorCode() 指令,但我不知道它应该在哪里,因为这段代码在 scala 2 中完美工作,并且这个错误只发生在我的 scala 3 项目中。真的很混乱

我一直在查看播放框架的文档,但我还没有发现任何与该错误远程相关的内容。 我有轻微的怀疑,我必须在有冲突的控制器内实例化“新的错误代码”,而不是在类中,但我可能是错的。 我将在这里留下导致错误的类的代码:

ErrorCode 只是一个抽象类,用于创建可扩展的类型。我尝试将其设为一个案例类,并使用 AccountException 案例类作为返回它的函数,但这并不是我的应用程序中真正需要的行为,我需要它返回某些类型。

abstract class ErrorCode {
    val code: String = ""
    val title: String = ""
    val detail: Option[String] = None
}

//case class ErrorCode(code: String, title: String, detail: Option[String])

object ErrorCode {
    def apply(codeValue: String, titleValue: String, detailValue: Option[String]): ErrorCode = new ErrorCode {
        override val code: String = codeValue
        override val title: String = titleValue
        override val detail: Option[String] = detailValue
    }

    def unapply(err: ErrorCode): Option[(String, String, Option[String])] =
        Some((err.code, err.title, err.detail))
}

这是 AccountException 异常,非常长:

abstract class AccountException(val errorCode: ErrorCode) {}

object AccountExceptions {
    def apply(errorCode: ErrorCode): AccountException = new AccountException(errorCode) {}

    def unapply(accEx: AccountException): Option[ErrorCode] = Some(accEx.errorCode)

    case class AccountNotFoundException(resource: String) extends AccountException(AccountNotFound(resource))

    case class AppNotFoundException(appKey: String) extends AccountException(AppNotFound(appKey))

    case class AppTackConfigurationException(resource: String) extends AccountException(AppTackConfigurationError(resource))

    case class InvalidCredentialsException(username: String) extends AccountException(InvalidCredentials(username))

    case class MalformedAppleTokenException(userResource: String) extends AccountException(MalformedAppleToken(userResource))

    case class MalformedGoogleTokenException(userResource: String) extends AccountException(MalformedGoogleToken(userResource))

    case class ResourceNotFoundException(resource: String) extends AccountException(ResourceNotFound(resource))

    case class UnknownException(username: String) extends AccountException(UnknownError(username))

    case class UserAlreadyExistsException(username: String) extends AccountException(UserAlreadyExists(username))

    case class UserNotFoundException(username: String) extends AccountException(UserNotFound(username))

    
}

object AccountErrorCodes {

    case class AccountNotFound(resource: String) extends ErrorCode
    {
        override val code: String = "OAC07"
        override val title: String = "Account not found"
        override val detail: Option[String] = Some(resource)
    }

    case class AppNotFound(appKey: String) extends ErrorCode
    {
        override val code: String = "AME01"
        override val title: String = "App not found"
        override val detail: Option[String] = Some(appKey)
    }

    case class AppTackConfigurationError(configErrorDetail: String) extends ErrorCode
    {
        override val code: String = "AME07"
        override val title: String = "Apptack configuration error"
        override val detail: Option[String] = Some(configErrorDetail)
    }

    case class InvalidCredentials(username: String)extends ErrorCode
    {
        override val code: String = "OAC02"
        override val title: String = "Credential invalid"
        override val detail: Option[String] = Some(username)
    }

    case class MalformedAppleToken(userResource: String)extends ErrorCode
    {
        override val code: String = "OAC06"
        override val title: String = "Malformed Apple sso token"
        override val detail: Option[String] = Some(userResource)
    }

    case class MalformedGoogleToken(userResource: String) extends ErrorCode
    {
        override val code: String = "OAC06"
        override val title: String = "Malformed google sso token"
        override val detail: Option[String] = Some(userResource)
    }

    case class ResourceNotFound(resource: String) extends ErrorCode
    {
        override val code: String = "OAC07"
        override val title: String = "Resource not found"
        override val detail: Option[String] = Some(resource)
    }

    case class UnknownError(username: String) extends ErrorCode 
    {
        override val code: String = "OAC05"
        override val title: String = "Unexpected error"
        override val detail: Option[String] = Some(username)
    }

    case class UserAlreadyExists(username: String) extends ErrorCode 
    {
        override val code: String = "OAC03"
        override val title: String = "User already exists"
        override val detail: Option[String] = Some(username)
    }

    case class UserNotFound(username: String) extends ErrorCode 
    {
        override val code: String = "OAC01"
        override val title: String = "User not found"
        override val detail: Option[String] = Some(username)
    }
}

我可以继续将 ErrorCode 从 AbstractClass 更改为 Case Class 方法,并更改返回 ErrorCode 的函数的 AccountErrorCodes case 类,但我使用这些特定类型来管理 HTTP 响应,所以我想保留这种行为:

//this function takes place in the controller
private def exceptionToResult(exception: AccountException): Result = {
        exception match {
            case e: AccountExceptions.AccountNotFoundException => NotFound(Json.toJson(exception))
            case e: AccountExceptions.AppTackConfigurationException => BadRequest(Json.toJson(exception))
            case e: AccountExceptions.InvalidCredentialsException => Unauthorized(Json.toJson(exception))
            case e: AccountExceptions.MalformedAppleTokenException => BadRequest(Json.toJson(exception))
            case e: AccountExceptions.MalformedGoogleTokenException => BadRequest(Json.toJson(exception))
            case e: AccountExceptions.ResourceNotFoundException => NotFound(Json.toJson(exception))
            case e: AccountExceptions.UnknownException => InternalServerError(Json.toJson(exception))
            case e: AccountExceptions.UserAlreadyExistsException => Conflict(Json.toJson(exception))
            case e: AccountExceptions.UserNotFoundException => NotFound(Json.toJson(exception))
            case _ => InternalServerError(Json.toJson(exception))
        }
    }
scala playframework scala-3 play-json
1个回答
0
投票

此代码在 scala 3 中编译并运行 3

import play.api.libs.json.{JsValue, Json, OWrites, Writes}
import play.api.mvc.Result
import play.api.mvc.Results.{InternalServerError, NotFound}

case class ErrorCode(code: String, title: String, detail: Option[String])

implicit val errorCodeWrites: OWrites[ErrorCode] = Json.writes[ErrorCode]

implicit val accountNotFoundExceptionWrites: OWrites[AccountException.AccountNotFoundException] =
  Json.writes[AccountException.AccountNotFoundException]

implicit val appNotFoundExceptionWrites: OWrites[AccountException.AppNotFoundException] =
  Json.writes[AccountException.AppNotFoundException]

//implicit val accountExceptionWrites: OWrites[AccountException] =
//  Json.writes[AccountException]

implicit val accountExceptionWrites: Writes[AccountException] = new Writes[AccountException]:
  def writes(o: AccountException): JsValue =
    val builder = Json.newBuilder
    builder += "code" -> o.errorCode.code
    builder += "title" -> o.errorCode.title
    builder += "detail" -> o.errorCode.detail
    builder.result()

enum AccountException(val errorCode: ErrorCode) {
  case AccountNotFoundException(resource: String)
      extends AccountException(
        ErrorCode(code = "OAC07", title = "Account Not Found", Some(resource))
      )

  case AppNotFoundException(appKey: String)
      extends AccountException(
        ErrorCode(code = "AME01", title = "App Not Found", Some(appKey))
      )
}

def exceptionToResult(exception: AccountException): Result = {
  exception match {
    case AccountException.AccountNotFoundException(_) =>
      NotFound(Json.toJson(exception))
    // ....
    case _ => InternalServerError(Json.toJson(exception))
  }
}

我没有转换你所有的代码。我希望它有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.