【问题标题】:Spring Boot Thymeleaf 3 external javascript template configurationSpring Boot Thymeleaf 3 外部 javascript 模板配置
【发布时间】:2018-02-18 17:24:09
【问题描述】:

我有一个作为 OAuth2 客户端工作的 Spring Boot 应用程序。

我使用 Thymeleaf 3 作为模板引擎。这些是我的 build.gradle 中与 Thymeleaf 3 相关的依赖项。

compile('org.thymeleaf:thymeleaf:3.0.1.RELEASE')
compile('org.thymeleaf:thymeleaf-spring4:3.0.1.RELEASE')
compile('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.4')
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.1.RELEASE')

如果我像这样引用依赖项没有区别:

ext["thymeleaf.version"] = "3.0.2.RELEASE"
ext["thymeleaf-layout-dialect.version"] = "2.1.1"
dependencies {
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
}

我希望能够使用 Thymeleaf 3 的 HTML 和 Javascript 模板模式。 HTML 模式有效,但在 javascript 模式下,messageSource 无法正常工作。

这是我的 WebMvcConfiguration 类:

@Configuration
public class WebMVCConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {

    private static final String CHARACTER_ENCODING = "UTF-8";
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Bean
    public ViewResolver htmlViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
        resolver.setContentType("text/html");
        resolver.setCharacterEncoding(CHARACTER_ENCODING);
        resolver.setViewNames(new String[] {"*.html"});
        return resolver;
    }

    @Bean
    public ViewResolver javascriptViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine(javascriptTemplateResolver()));
        resolver.setContentType("application/javascript");
        resolver.setCharacterEncoding(CHARACTER_ENCODING);
        resolver.setViewNames(new String[] {"*.js"});
        return resolver;
    }

    private TemplateEngine templateEngine(ITemplateResolver templateResolver) {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver);
        return engine;
    }

    private ITemplateResolver htmlTemplateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("templates/");
        resolver.setCacheable(false);
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setSuffix(".html");
        return resolver;
    }

    public ITemplateResolver javascriptTemplateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("classpath:/static/js/");
        resolver.setCacheable(false);
        resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
        // resolver.setSuffix(".js");
        return resolver;
    }
}

请注意,我必须在 javascriptTemplateResolver 中使用“classpath:/static/js/”,因为当我只使用“static/js/”时,会出现以下异常:

java.io.FileNotFoundException: Could not open ServletContext resource [/static/js/headerconfig.js]

我还必须注释掉 setSuffix,因为有了它我得到以下异常:

java.io.FileNotFoundException: class path resource [static/js/typeutils.js.js] cannot be opened because it does not exist

我认为这已经表明了主要问题,但我不知道是什么原因造成的。


我有一个用于处理 javascript 模板的控制器:

@Controller
public class JavascriptController {

    @RequestMapping(method = RequestMethod.GET, value = "/js/{template}.js")
    public String jsMapping(@PathVariable("template") String template, Model model) {
        model.addAttribute("myAttribute", "Attribute works!");
        return template;
    }
}

我的 messages.properties 文件位于

  • src/main/resources/messages.properties
  • src/main/resources/messages_hu.properties

我在 src/main/resources/templates/ 文件夹中有一个 HTML 文件,它引用了一个 javascript 文件,如下所示:

<script th:src="@{js/typeutils.js}"></script>

引用的 javascript 文件 (js/typeutils.js):

var a = [[${myAttribute}]];
var b = [[#{test}]];
console.log(a);
console.log(b);

当我运行应用程序并检查 javascript 控制台时,打印出来的内容如下:

Attribute works!
??test_hu_HU??

因此模型属性已成功传递给 javascript 文件,并且已检测到本地化,但未找到“测试”的消息。 似乎 javascript 模板模式的行为与 HTML 模板模式完全不同。

如果修复 javascript 模板模式配置以使其也能处理 messages.properties 文件,应该如何解决?

谢谢!

【问题讨论】:

  • 你可以在浏览器中使用inspect元素来检查你的模板脚本src标签中有什么值
  • 它将脚本标签替换为普通脚本标签: 虽然没有 type="text/javascript " 属性。
  • 你的 js 文件在 static 文件夹内的 js 文件夹中,你的 html 在 templates 文件夹中。正确的 !。您可以从模板访问您的 js 文件。像这样: .. 我添加了 / 。请试试这个
  • 我已经尝试过了,但我得到了相同的结果:模型属性被插入但消息没有。

标签: java spring spring-boot thymeleaf


【解决方案1】:

我找到了解决方案(通过查看 thymeleaf 源代码),这可能并不完美,但它确实有效!

与Baeldung给出的example不同,以下解决了我的问题:

@Configuration
public class WebMVCConfig implements WebMvcConfigurer, ApplicationContextAware {
    private static final String CHARACTER_ENCODING = "UTF-8";
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Bean
    public ViewResolver htmlViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setContentType("text/html");
        resolver.setCharacterEncoding(CHARACTER_ENCODING);
        resolver.setViewNames(new String[] { "*.html" });
        return resolver;
    }

    @Bean
    public ViewResolver javascriptViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setContentType("application/javascript");
        resolver.setCharacterEncoding(CHARACTER_ENCODING);
        resolver.setViewNames(new String[] { "*.js" });
        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setMessageSource(messageSource());
        engine.addTemplateResolver(htmlTemplateResolver());
        engine.addTemplateResolver(javascriptTemplateResolver());
        return engine;
    }

    private ITemplateResolver htmlTemplateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setOrder(0);
        resolver.setCheckExistence(true);
        resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("classpath:templates/");
        resolver.setCacheable(false);
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setSuffix(".html");
        return resolver;
    }

    public ITemplateResolver javascriptTemplateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(applicationContext);
        resolver.setOrder(1);
        resolver.setCheckExistence(true);
        resolver.setPrefix("classpath:/static/js/");
        resolver.setCacheable(false);
        resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
        return resolver;
    }

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource msgSource = new ResourceBundleMessageSource();
        msgSource.setAlwaysUseMessageFormat(false);
        msgSource.setBasename("messages");
        msgSource.setDefaultEncoding(CHARACTER_ENCODING);
        msgSource.setFallbackToSystemLocale(true);
        msgSource.setUseCodeAsDefaultMessage(false);
        return msgSource;
    }
}

基本上我们将 templateEngine 设置为一个 bean,以便我们覆盖 ThymeleafDefaultConfiguration 的默认 templateEngine 实现 bean。这意味着每次需要解析模板时,肯定会使用相同的 templateEngine。

我们将htmlTemplateResolver的顺序设置为0,将javascriptTemplateResolver的顺序设置为1,这样我们将尝试先在HTML模板模式下解析每个模板,然后在Javascript模式下。

同样重要的是,我们将 SpringResourceTemplateResolver 的 checkExistence 标志设置为 true,因为这样如果找不到模板,我们将尝试使用下一个 TemplateResolver。

这个方案有一个缺点,就是我们会先尝试在 HTML 模式下解析不必要的 javascript 模板,然后在 JAVASCRIPT 模式下,所以在解析 javascript 资源时多了一个步骤。

我会尝试更好地解决问题,但现在,这对我有用。

【讨论】:

    猜你喜欢
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-15
    • 2017-11-26
    • 2019-10-31
    • 2017-07-30
    • 2018-09-24
    • 2019-08-17
    相关资源
    最近更新 更多