【问题标题】:Thymeleaf unable to load static resourcesThymeleaf 无法加载静态资源
【发布时间】:2018-09-11 07:26:30
【问题描述】:

我有一个 Spring Web 应用程序可以正确加载模板,但不加载 CSS 和图像等静态资源。我已经在 src/main/resources/static 目录中添加了我的静态资源,添加了 thymeleaf 注释,添加了配置,并在 * 上针对类似问题检查了它们,但是我的 CSS/图像文件都没有被加载:

控制台错误

Refused to apply style from 'http://localhost:8080/login' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

目录结构

TemplateConfig.java

package com.valencra.recipes.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;

@Configuration
public class TemplateConfig {
  @Bean
  public SpringResourceTemplateResolver templateResolver() {
    final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
    templateResolver.setPrefix("classpath:/templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("LEGACYHTML5");
    return templateResolver;
  }

  @Bean
  public SpringTemplateEngine templateEngine() {
    final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
    springTemplateEngine.addTemplateResolver(templateResolver());
    springTemplateEngine.addDialect(new SpringSecurityDialect());
    return springTemplateEngine;
  }

  @Bean
  public ThymeleafViewResolver viewResolver() {
    final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setTemplateEngine(templateEngine());
    viewResolver.setOrder(1);
    return viewResolver;
  }
}

layout.html

<!DOCTYPE html>
<html lang="en">

<head th:fragment="head">

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">

    <title>My Recipes</title>

    <link href='https://fonts.googleapis.com/css?family=Varela+Round' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" th:href="@{/css/unsemantic-grid-responsive.css}">
    <link rel="stylesheet" th:href="@{/css/styles.css}">

</head>

<body>

    <nav th:fragment="nav">
        <a th:href="@{|/profile|}" th:text="${currentUser.name}">
            Chandra S.
        </a>
        &bull;
        <form th:action="@{/logout}" method="post" style="display: inline">
            logout
        </form>
    </nav>

    <div th:fragment="home">
        <div class="grid-100">
            <a th:href="@{|/|}">
                <h1>
                    <img th:src="@{/images/chefbot.svg}" height="60px">
                    <br>
                    My Recipes
                </h1>
            </a>
        </div>
    </div>

    <div th:fragment="favorite">
        <form th:action="@{|/recipes/${recipe.id}/favorite|}" method="post" style="display:inline">
            <button type="submit">
                <img th:src="${recipe.isFavorite(currentUser)} ? @{/images/favorited.svg} : @{/images/favorite.svg}" style="height: 15px;">
            </button>
        </form>
    </div>

</body>

</html>

login.html

<!doctype html>
<html lang="en">

<head th:replace="layout :: head"></head>

<body>

    <nav>
        <a th:href="@{/signup}">
            sign-up
        </a>
    </nav>

  <div class="grid-container">

    <div th:replace="layout :: home"></div>

    <div class="grid-100">
      <div class="recipes">

        <form th:action="@{|/login|}" method="post" th:object="${user}">
          <div class="prefix-20 grid-60 suffix-20">
            <p>
              <input placeholder="Username" th:field="*{username}"> </input>
            </p>
          </div> <div class="clear"></div>
          <div class="prefix-20 grid-60 suffix-20">
            <p>
              <input placeholder="Password" type="password" th:field="*{password}"> </input>
            </p>
          </div> <div class="clear"></div>
          <div class="prefix-20 grid-60 suffix-20">
            <p>
              <button>Login</button>
            </p>
          </div> <div class="clear"></div>
        </form>

      </div> <!-- recipes -->
    </div> <!-- grid-100 -->

  </div> <!-- grid-container -->

</body>

</html>

【问题讨论】:

  • 请添加错误信息
  • 你有什么错误吗?打开浏览器检查选项卡。您是否看到任何 4xx5xx 错误?
  • 请查看更新后的问题
  • 所以我尝试添加type='text/html'(如*.com/questions/48248832/… 中所建议的那样),错误消失了,但没有一个css get 被加载,它们的状态码都是302。

标签: spring thymeleaf


【解决方案1】:

看起来您的 CSS 是作为 HTML 提供的。

既然您将样式表放在src/main/resources/static 中,Spring 应该根据合理的默认值设置大部分内容,包括设置正确的内容类型标头。

我假设您使用的是 Spring Security,因为您显示了一个登录表单。

可能是您的资源受到 Spring Security 的保护。因此,您得到的不是您的样式表,而是30x 重定向到登录页面。这可以解释为什么您的浏览器抱怨 text/html 内容类型,这对于 css 是错误的,但对于登录页面是正确的类型。

您是否在项目中的某处配置了HttpSecurity(很可能带有@EnableWebSecurity 注释)?

如果是这样,您需要允许匿名访问您的资源:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/css/**", "/images/**").permitAll()
            // rest of your code 

如果您在浏览器控制台中切换到网络选项卡,您应该能够检查到登录页面的重定向。

【讨论】:

  • 非常感谢@phisch!你说得对,我使用弹簧安全并覆盖该方法。我刚刚将排除项添加到我的 http 构建器中。
【解决方案2】:

对我来说有帮助的是@phisch 和以下代码的回答

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(
            "/webjars/**",
            "/img/**",
            "/css/**",
            "/js/**")
            .addResourceLocations(
                    "classpath:/META-INF/resources/webjars/",
                    "classpath:/static/img/",
                    "classpath:/static/css/",
                    "classpath:/static/js/");
     }

}

【讨论】:

    最近更新 更多