我正在為如何將命令式風格轉換為功能風格而苦苦掙扎。
在命令式 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 或繞過業務層。我覺得我有 afor然后 a if,然后 amatch并且回應都亂七八糟地混在一起。我在這里撰寫難以理解的代碼,我希望一些 scala 大師可以告訴我如何清理它并使其可讀。
uj5u.com熱心網友回復:
恕我直言,問題根源在于您對資料進行建模的方式;主要是validateRequest
Always remember, parse, don't validate。
此外,我會使用這樣的主處理程式進行無型別錯誤路由:
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[_]。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/508560.html
