当前位置: 首页 > 知识库问答 >
问题:

在控制器外部渲染scala模板?(第二场)

吉玉宸
2023-03-14

我正试图将发送通知电子邮件与导致它们的事件分离开来。到目前为止,我正在将一个邮件对象(DocumentIssuedMail)从控制器传递给Akka actor(EmailDispatcher),然后它通过play mailer插件的play easymail包装发送邮件。邮件正文由邮件对象在传递给参与者后生成,HTML由Scala模板生成。

此模板包含带有绝对URL的链接,通过调用

@routes.SomeController.someAction().absoluteURL()

但是,在尝试呈现模板时,我遇到了RuntimeException。

堆栈跟踪如下所示:

java.lang.RuntimeException: There is no HTTP Context available from here.
    at play.mvc.Http$Context.current(Http.java:30)
    at play.mvc.Http$Context$Implicit.ctx(Http.java:196)
    at play.core.j.PlayMagicForJava$.requestHeader(TemplateMagicForJava.scala:56)
    at views.html.email._learner_main$.apply(_learner_main.template.scala:41)
    at views.html.documents.email.new_doc_unregistered$.apply(new_doc_unregistered.template.scala:47)
    at views.html.documents.email.new_doc_unregistered$.render(new_doc_unregistered.template.scala:67)
    at views.html.documents.email.new_doc_unregistered.render(new_doc_unregistered.template.scala)
    at email.DocumentIssuedMail.getUnregisteredMail(DocumentIssuedMail.java:71)
    at email.DocumentIssuedMail.getMail(DocumentIssuedMail.java:67)
    at actors.email.EmailDispatcher.onReceive(EmailDispatcher.java:32)
    at akka.actor.UntypedActor$$anonfun$receive$1.applyOrElse(UntypedActor.scala:167)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)
    at akka.actor.ActorCell.invoke(ActorCell.scala:456)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)
    at akka.dispatch.Mailbox.run(Mailbox.scala:219)
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

是否可以在该位置渲染模板,或者我需要在原始线程上进行渲染?

共有1个答案

任伟
2023-03-14

这个问题的一个可能解决方案是将http请求显式地传递给参与者,然后再传递给邮件模板。

在模板中,将此请求传递给absoluteURL()方法:

@(requestHeader: play.api.mvc.RequestHeader)

@main("") {
    @routes.Application.someAction().absoluteURL()(requestHeader)
}

除了DocumentIssuedMail,您还需要向演员传递请求。这里是一个简单的DTO。

import play.api.mvc.RequestHeader;

public class DocumentIssuedMailWrapper {
    private DocumentIssuedMail documentIssuedMail;
    private RequestHeader requestHeader;

    public DocumentIssuedMailWrapper(DocumentIssuedMail documentIssuedMail, RequestHeader requestHeader) {
        this.documentIssuedMail = documentIssuedMail;
        this.requestHeader = requestHeader;
    }

    public DocumentIssuedMail getDocumentIssuedMail() {
        return documentIssuedMail;
    }

    public RequestHeader getRequestHeader() {
        return requestHeader;
    }
}

参与者将请求作为普通参数从DTO传递到邮件模板。

import akka.actor.UntypedActor;
import play.api.templates.Html;
import views.html.mail;

public class EmailDispatcher extends UntypedActor {

    @Override
    public void onReceive(Object message) throws Exception {
        if (message instanceof DocumentIssuedMailWrapper) {
            DocumentIssuedMailWrapper wrapper = (DocumentIssuedMailWrapper) message;
            Html mailTemplate = mail.render(wrapper.getRequestHeader());
            //sending mail
        }
    }

}

在控制器中,您可以通过调用ctx()来获取请求_requestHeader()方法。现在,您只需要与演员安排一个工作,并通过DTO传递请求。

import akka.actor.ActorRef;
import akka.actor.Props;
import play.libs.Akka;
import play.mvc.*;

import scala.concurrent.duration.Duration;

import java.util.concurrent.TimeUnit;

public class Application extends Controller {

    public static Result sendMail() {
        DocumentIssuedMailWrapper wrapper = new DocumentIssuedMailWrapper(new DocumentIssuedMail(), ctx()._requestHeader());

        ActorRef emailDispatcher = Akka.system().actorOf(Props.create(EmailDispatcher.class));
        Akka.system().scheduler().scheduleOnce(Duration.create(0, TimeUnit.MILLISECONDS), emailDispatcher, wrapper, Akka.system().dispatcher(), null);
        return ok("Mail sent");
    }

    public static Result someAction() {
        return ok("Some other action");
    }

}
 类似资料:
  • 模板渲染 在控制器里面模板渲染,可以使用基类封装好的 fetch 方法; ThinkCMF5 设置的模板文件名分隔符是/,模板后缀名为html 常见 fetch 调用方法如下: 1.不带任何参数: return $this->fetch(); 系统会按照默认规则自动定位模板文件,其规则是: 当前主题目录/当前应用/当前控制器(小写+下划线)/当前操作(小写)+.html 2.指定渲染当前控制器

  • 模板渲染 在控制器里面模板渲染,可以使用基类封装好的 fetch 方法; ThinkCMF6.0 设置的模板文件名分隔符是/,模板后缀名为html 常见 fetch 调用方法如下: 1.不带任何参数: return $this->fetch(); 系统会按照默认规则自动定位模板文件,其规则是: 当前主题目录/当前应用/当前控制器(小写+下划线)/当前操作(小写)+.html 2.指定渲染当前控

  • 问题内容: 我有一个Rails应用程序尝试合并一些AJAX,在其中单击“新建”将打开一个模式窗口和一个表单。我希望能够显示验证错误(如果失败),因此在我的create动作中,我考虑过重新呈现new.js.erb文件。这是正确的方法吗? 我得到的结果是浏览器中的转义js文本,例如: 我尝试将各种选项放入渲染块,但没有运气。有小费吗? 问题答案: 最好的做法是同时支持AJAX和Non-AJAX调用,以

  • 问题内容: 我正在尝试在angularjs中实现一个插件系统,该系统将允许用户配置他们将在特定页面上看到的“小部件”。每个窗口小部件均由控制器和模板(URL)定义。是否可以创建一个实例化控制器,使用模板调用它并包含结果内容的指令? 目标是这样的: 问题答案: 有两种方法可以做到这一点; 一种是使用已经可用的helper指令(例如和),第二种是手动的;手动版本 可能 会更快,但我不确定。 简单方法:

  • 6.1 渲染模板 一旦你拥有一个模版文件,你可以通过给一个map来给它传递数据。 map是一个变量及赋予的值的集合,模板使用它来得到变量的值,或者对于块标签求值。 它的渲染函数有一个可选的变量键值对map 通过 ctx.Render() 方法来渲染模板,例如: func (r *Render) Serve(ctx *faygo.Context) error { return ctx.Ren

  • Tango默认核心不包含模板渲染功能,在官方中间件中包含两个渲染引擎中间件,一个是 Go标准模板引擎, 另一个是 Pongo2模板引擎