簡體   English   中英

如何為 http4s 編寫慣用的 scala 代碼

[英]How to write idiomatic scala code for http4s

我正在為如何將命令式風格轉換為功能風格而苦苦掙扎。

在命令式 web 請求中,我習慣於說以下偽代碼:

public Response controllerAction(Request request) {
    val (req, parserErrors) = parser.parseRequest(request);
    if (parserErrors.any()) {
        return FourHundredError(parserErrors);
    }
    
    val businessErrors = model.validate(req);
    if (businessErrors.any()){
        return FourOhFour(businessErrors);
    }

    val (response, errorsWithOurStuff) = model.doBusinessLogicStuff(req);
    if (errorsWithOurStuff.any()) {
        return FiveHundredError(errorsWithOurStuff);
    }

    return OK(response)
}

我正在嘗試使用 http4s 將其轉換為功能樣式。

  def businessRoutes[F[_]: Sync](BL: BusinessLogic[F]): HttpRoutes[F] = {
    val dsl = new Http4sDsl[F]{}
    import dsl._
    HttpRoutes.of[F] {
      case req @ POST -> Root / "sms" =>
        for {
          request <- req.as[BL.BuisnessRequest]
          requestErrors <- BL.validateRequest(request)
          response <- if (requestErrors.isEmpty) {
            BL.processRequest(request) match {
              case Failure(e) => InternalServerError(e)
              case Success(response) => Ok(response)
            }
          } else {
            BadRequest(requestErrors)
          }
        } yield response
    }
  }

上面的代碼看起來......對我來說很糟糕,我不知道如何讓它變得更好。 我的目標是保留此處包含的所有 http 樣式抽象,因為我不想泄漏 http4s 或繞過業務層。 我覺得我有一個for then 一個if ,然后是一個match並且響應都亂七八糟地混在一起。 我在這里編寫難以理解的代碼,我希望一些 scala 大師可以告訴我如何清理它並使其可讀。

恕我直言,問題根源在於您對數據進行建模的方式; 主要用validateRequest
永遠記住, 解析,不要驗證

此外,我會 go 使用這樣的主處理程序將無類型錯誤路由:

import cats.syntax.all._
import io.circe.{Error => CirceError}

object model {
  final case class RawRequest(...)
  final case class BuisnessRequest(...)
  final case class BuisnessResponse(...)
}

object errors {
  // Depending on how you end up using those,
  // it may be good to use scala.util.control.NoStackTrace with these.
  // They may also be case classes to hold some context.
  final case object ValidationError extends Throwable
  final case object BusinessError extends Throwable
}

trait BusinessLogic {
  def validateRequest(rawRequest: RawRequest): IO[BusinessRequets]
  def processRequest(request: BusinessRequets): IO[BuisnessResponse]
}

final class HttpLayer(bl: BusinessLogic) extends Http4sDsl[IO] {
  private final val errorHanlder: PartialFunction[Throwable, IO[Response[IO]] = {
    case circeError: CirceError =>
      BadRequest(...)

    case ValidationError =>
      NotFound(...)

    case BusinessError =>
      InternalServerError(...)
  }

  val routes: HttpRoutes[IO] = HttpRoutes[F] {
     case req @ POST -> Root / "sms" =>
       req
        .as[RawRequest] // This may fail with CirceError.
        .flatMap(bl.validateRequest) // This may fail with ValidationError.
        .flatMap(bl.processRequest) // This may fail with BusinessError.
        .redeemWith(recover = errorHandler, response => Ok(response))
  }
}

為了簡單起見,我在這里使用了具體的IO ,如果您願意,可以使用F[_]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM