如何避免"java.lang.RuntimeException: There is no HTTP Context available from here"?

How to avoid "java.lang.RuntimeException: There is no HTTP Context available from here"?

我在发送有关用户操作的通知电子邮件时遇到 Play Framework 2.5 异常。这是代码:

public class IpnListener extends Controller {
   @Inject
   private Notif notif;

   @Inject
   private WSClient ws;

   public CompletionStage<Result> validate() {
       return ws.url(/*...*/)
         .thenApply(response -> {
             /* do some validations */
             notif.send(ctx(), notification, user.getEmail());
          });
   }
}

Notif class.

public class Notif {
  @Inject
  private MailerClient mailerClient;

  @Inject
  private HttpExecutionContext executionContext;

  public void send(Http.Context context, Notification notification, String target) {
      /* create an email with template*/
      CompletableFuture.runAsync(() -> {
           try {
               mailerClient.send(email);
           }
           catch (Exception e) {
               e.printStackTrace();
           }
        },
        executionContext.current());
  }
}

然后我随机得到这个异常:

java.lang.RuntimeException: There is no HTTP Context available from here.
    at play.mvc.Http$Context.current(Http.java:62)
    at play.mvc.Controller.ctx(Controller.java:27)
    at controllers.listeners.IpnListener.lambda$validate[=13=](IpnListener.java:69)
    at services.payment.PayPal$Request.lambda$verify[=13=](PayPal.java:201)
    at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616)
    at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591)
    at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:457)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
java.lang.RuntimeException: There is no HTTP Context available from here.
    at play.mvc.Http$Context.current(Http.java:62)
    at play.mvc.Controller.ctx(Controller.java:27)
    at controllers.listeners.IpnListener.lambda$validate[=13=](IpnListener.java:69)
    at services.payment.PayPal$Request.lambda$verify[=13=](PayPal.java:201)
    at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616)
    at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591)
    at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:457)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

我正在考虑将 ctx() 值保存在这样的变量中,但我不确定它的生命周期:

public class IpnListener extends Controller {
   @Inject
   private Notif notif;

   @Inject
   private WSClient ws;

   public CompletionStage<Result> validate() {
       Http.Context context = ctx();
       return ws.url(/*...*/)
         .thenApply(response -> {
             /* do some validations */
             notif.send(context, notification, user.getEmail());
          });
   }
}

你需要

@Inject HttpExecutionContext ec;

into your controller. On the thenApply() call, supply ec.current() as the executioner.

.thenApply(response -> {

}, ex.current());

WSClient 调用的 thenApply() 方法中没有可用的 HTTP 上下文,因此我不得不将 HttpExecutionContextthenApplyAsync() 一起使用。

public class IpnListener extends Controller {
   @Inject
   private Notif notif;

   @Inject
   private WSClient ws;

   @Inject
   private HttpExecutionContext httpExecutionContext;

   public CompletionStage<Result> validate() {
       Http.Context context = ctx();
       return ws.url(/*...*/)
         .thenApplyAsync(response -> {
             /* do some validations */
             notif.send(context, notification, user.getEmail());
          },
          httpExecutionContext.current());
   }
}