2016-11-14 6 views
2

私はScalaとPlayでこれらの概念のいくつかを本当に苦労しています。私は自分のデータベースを更新したいと思います。将来私のデータベース更新機能をラップする必要があると思いますが、必要なものを返す方法がわかりませんplay.api.mvc.ResultScalaのプレイアクションと先を理解する

私はいくつかの応答を返すスカラ座でのコントローラがあります。

def somePath = MyCustomAction.async(parse.tolerantJson) { implicit request => 
    request.body.validate[MyObject].map { myObject => 
     for { 
      getSomething <- getSomethingFuture 
      getSomethingElse <- getSomethingElseFuture 
     } yield { 
      if (getSomethingElse) { 
       if (getSomething) 
        updateMyDatabase(myObject)//perform database request 
       else 
        BadRequest("Invalid request") 
      } 
     } else { 
      // do some other things 
     } 
    } 
} 

private [this] def updateMyDatabase(myObject: MyObject) { 
    // do things to update the database 
} 

Resultを返すことがupdateMyDatabaseべきか?私はそれを未来に包み込み、それが正常に完了したかどうかを確認したいのですか?私が確認したら、正しいResultSuccessメソッドに返しますか?

今、私はこれらのものをどのように構成するか、実際にはFutureベースのソリューションを実装する方法を理解していません。

+0

、型チェッカーを満足する以外、将来的にそれを包むにはポイントがありません。 – rethab

+0

@rethab無知には申し訳ありませんが、将来的にはそれをラップして非同期プールに入れませんか?さもなければ、要求はブロックされますか? – NeedingHelp

+0

@rethabまた、私は操作の成功または失敗を返したいと思います。 SomeExceptionをスローするだけでいいですか?その慣用的で最高のアーチですか? – NeedingHelp

答えて

1

あなたのupdateMyDatabase関数は、成功したかどうかを知るために、何もユニット値を返さないでください。データベースアクションが返すことができる複数の応答があります。

  1. データベースエラー、例外スロー
  2. 行が見つからない、何も更新が見つから
  3. 行を発生しない、ので、試しに

を更新[ブール]は、これらすべてのシナリオを処理するのに適したタイプです。

private [this] def updateMyDatabase(myObject: MyObject): Try[Boolean] = { 
    // do things to update the database 
} 

これで、レスポンスが一致し、正しい結果タイプが返されるようになりました。

updateMyDatabase(myObject) match { 
    case Failure(exception) => BadRequest 
    case Success(b) => if (b) Ok else BadRequest 
} 

getSomethingFutureとgetSomethingElseFuturesは両方の先物を戻ってきているので、あなたはすでに将来のコンテキスト内で作業している、そして未来のあなたの結果のいずれかをラップする必要はありません。 yieldキーワードは、降伏のボディ内の何かを未来に戻すことを確実にします。

これで、getSomethingFutureまたはgetSomethingElseFutureが失敗する状況を処理する必要があります。これを行うには、recover関数を使用します。プレイdocumentationから

(for { 
    getSomething <- getSomethingFuture 
    getSomethingElse <- getSomethingElseFuture 
} yield { 
    // this code only executes if both futures are successful. 
    updateMyDatabase(myObject) match { 
     case Failure(exception) => BadRequest 
     case Success(b) => if (b) Ok else BadRequest 
    } 
}) recover { 
    // Here you can match on different exception types and handle them accordingly. 
    // So throw a specific exception for each task if you need to handle their failures differently. 
    case e: GetSomethingFutureFailed => BadRequest 
    case e: GetSomethingElseFutureFailed => BadRequest 
    case _ => BadRequest 
} 

:だからあなたの最終的なコードは次のようになりますあなたはそれゆえ先物であなたのブロッキングコードをラップするために誘惑されることに注意してください。これはノンブロッキングではありません。ブロッキングが別のスレッドで発生することを意味します。使用しているスレッドプールにブロッキングを処理するのに十分なスレッドがあることを確認する必要があります。

また、あなたがそうのような実行コンテキストを注入するためにあなたのコントローラに指示していることを確認しますsooteの答え@

import scala.concurrent.ExecutionContext 
class AsyncController @Inject() (...)(implicit exec: ExecutionContext) 
+0

playのデフォルトの実行コンテキストをインポートすることは、基本的なグローバル状態に依存するため、基本的にはお勧めできません。あなたはそれを注入する必要があります。 – rethab

+0

どのようにデフォルトの実行コンテキストを注入できますか?ドキュメントには、インポートの例だけが示されており、カスタム実行コンテキストに対してのみインジェクションが使用されています。 – soote

+0

単に 'scala.concurrent.ExecutionContext'を宣言してください。 Playは 'play.api.inject.BuiltinModule'のバインディングを定義します。 – rethab

0

は動作しますが、将来を返すupdateMyDatabase持っている一つの理由は、あなたがFuture#recoverを使用してエラー処理を統一することができます。もう一つの理由は、プレイは一種のupdateMyDatabaseFuture[_]を返す場合は、このような何かを行うことができ、ブロッキング操作がFuture

で行われることを想定ので、あなたは、他の場所での方法を再利用することができます:

def somePath = MyCustomAction.async(parse.tolerantJson) { implicit request => 
    request.body.validate[MyObject].map { myObject => 
     val futResult = for { 
      getSomething <- getSomethingFuture 
      getSomethingElse <- getSomethingElseFuture 
      if getSomethingElse 
      if getSomething 
      _ <- updateMyDatabase(myObject) 
     } yield Ok("") 

     futResult.recover { 
      case e: Exception1 => BadRequest("bad request") 
      case e: Exception2 => BadRequest("blah") 
      case e => InternalServerError(e.getMessage) 
     } 
    } 
} 

private [this] def updateMyDatabase(myObject: MyObject): Future[Unit] = ??? 

あなたはエラーをより細かく処理する方法が不思議に思えるかもしれません。ここで

def somePath = MyCustomAction.async(parse.tolerantJson) { implicit request => 
    request.body.validate[MyObject].map { myObject => 
     val futResult = for { 
      getSomething <- getSomethingFuture 
      getSomethingElse <- getSomethingElseFuture 
      _ <- if(getSomethingElse && getSomething) { 
        updateMyDatabase(myObject) 
       } else { 
        Future.failed(new CustomException("Couldn't get something else")) 
       } 
     } yield Ok("") 

     futResult.recover { 
      case e: CustomException => BadRequest("failed to get something else") 
      case e: Exception2 => BadRequest("blah") 
      case e => InternalServerError(e.getMessage) 
     } 
    } 
} 

が代替方法です:ここでは一つの方法だデータベース機能は、将来を返さない場合

def somePath = MyCustomAction.async(parse.tolerantJson) { implicit request => 
    request.body.validate[MyObject].map { myObject => 
     val futResult = for { 
      getSomething <- getSomethingFuture 
      getSomethingElse <- getSomethingElseFuture 
      result <- if(getSomethingElse && getSomething) { 
         updateMyDatabase(myObject).map(_ => Ok("")) 
         } else { 
         Future.successful(BadRequest("failed to get something else")) 
         } 
     } yield result 

     futResult.recover { 
      case e: Exception2 => BadRequest("blah") 
      case e => InternalServerError(e.getMessage) 
     } 
    } 
} 
関連する問題