Angular 与 Spring boot 静态不起作用

发布于 2025-01-12 23:49:33 字数 3130 浏览 0 评论 0原文

有角度的前端

我有一个 Spring Boot 应用程序,其中包含一个像这样的

: src/main/resources/static/zanori2

在 zanori2 中,我有 ng build 的结果,例如:

index.html、index.html。 js,favico.ico 等

我尝试了这个resourceHandle:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    /*@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {*/
        //registry.addResourceHandler("/**/*")
        /*.addResourceLocations("classpath:/static/zanori2/")
        .resourceChain(true)
        .addResolver(new PathResourceResolver() {
            @Override
            protected Resource getResource(String resourcePath,
                Resource location) throws IOException {
                Resource requestedResource = location.createRelative(resourcePath);
                return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                : new ClassPathResource("/static/zanori2/index.html");
            }
        });
    }
}

但是当我转到: localhost:8080/zanori2/index.html 它返回我localhost:8080 并且 js 文件有效。

然而,这很奇怪,因为我不允许共享网址,因为如果我直接访问 localhost:8080 我会得到一个未找到的页面。

使用其他配置:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private Environment env;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        /* Caching strategy */
        boolean prodMode = Arrays.asList(env.getActiveProfiles()).contains("pro");
        Integer cachePeriod = prodMode ? null : 0;
        boolean useResourceCache = prodMode;

        VersionResourceResolver versionResourceResolver = new VersionResourceResolver();
        versionResourceResolver.addContentVersionStrategy("/**/*.js", "/**/*.css");
        AppCacheManifestTransformer transformer = new AppCacheManifestTransformer();

        /* robots.txt */
        registry.addResourceHandler("/robots.txt")
                .addResourceLocations("classpath:/static/robots.txt");

        /* All other resources */
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/zanori2")
                .setCachePeriod(cachePeriod)
                .resourceChain(useResourceCache)
                .addResolver(versionResourceResolver)
                .addTransformer(transformer);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        /* Make sure Thymeleaf views are not accessible directly as static resources */
        registry.addRedirectViewController("/app/*.html", "/");
        /* Default mapping */
        registry.addRedirectViewController("/", "/app/index.html");
        /* Application entry */
        registry.addViewController("/app/index.html").setViewName("index");
    }
}

我转到 localhost:8080/zanori2/index.html 并保留相同的网址,但是找不到我的 js 文件,因此也不起作用。

我没有找到任何可以正常工作的例子。

问题示例: 输入图片此处描述

I have an spring boot app, which contains an angular front

like this:

src/main/resources/static/zanori2

Where in zanori2 I have the result of ng build some like:

index.html, index.js, favico.ico and so on

I tried this resourceHandle:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    /*@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {*/
        //registry.addResourceHandler("/**/*")
        /*.addResourceLocations("classpath:/static/zanori2/")
        .resourceChain(true)
        .addResolver(new PathResourceResolver() {
            @Override
            protected Resource getResource(String resourcePath,
                Resource location) throws IOException {
                Resource requestedResource = location.createRelative(resourcePath);
                return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                : new ClassPathResource("/static/zanori2/index.html");
            }
        });
    }
}

However when I go to: localhost:8080/zanori2/index.html it return me to localhost:8080 and the js files works.

However it is weird because I am not allowed to share the url because if I go directly to localhost:8080 I get a not found page.

And with this other configuration:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private Environment env;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        /* Caching strategy */
        boolean prodMode = Arrays.asList(env.getActiveProfiles()).contains("pro");
        Integer cachePeriod = prodMode ? null : 0;
        boolean useResourceCache = prodMode;

        VersionResourceResolver versionResourceResolver = new VersionResourceResolver();
        versionResourceResolver.addContentVersionStrategy("/**/*.js", "/**/*.css");
        AppCacheManifestTransformer transformer = new AppCacheManifestTransformer();

        /* robots.txt */
        registry.addResourceHandler("/robots.txt")
                .addResourceLocations("classpath:/static/robots.txt");

        /* All other resources */
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/zanori2")
                .setCachePeriod(cachePeriod)
                .resourceChain(useResourceCache)
                .addResolver(versionResourceResolver)
                .addTransformer(transformer);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        /* Make sure Thymeleaf views are not accessible directly as static resources */
        registry.addRedirectViewController("/app/*.html", "/");
        /* Default mapping */
        registry.addRedirectViewController("/", "/app/index.html");
        /* Application entry */
        registry.addViewController("/app/index.html").setViewName("index");
    }
}

I go to localhost:8080/zanori2/index.html and I keep in the same url however my js files are not found so is not working too.

I do not found any example of this working properly.

Example of the problem:
enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

<逆流佳人身旁 2025-01-19 23:49:33

静态资源的映射

Spring 将自动在多个位置搜索与 Web 配置中的任何控制器或其他设置不匹配的路径。当前默认情况下会检查这些位置:

classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/

您可以通过设置

spring.web.resources.static-locations

应用程序属性来覆盖此行为。

从一个干净的项目开始并添加:

spring.web.resources.static-locations=classpath:/static/zanori2/

到您的application.properties将使Spring搜索静态资源的预期路径。

将单页前端中的路径映射到 /

请记住,在下面的所有内容中,到静态资源的映射已被上面的内容更改,因此在下面,路径 /a/b 将真正获取 /static/zanori2/a/b。另请记住,Spring 控制器始终优先于静态资源,因此如果您定义的 Spring 控制器会干扰静态路径,则会使用它。

如果您还支持单页前端使用内部路由,您需要向 WebMvcConfigurer 添加一些配置。技巧是仍然从其真实位置加载所有静态内容,但将单页应用程序内的所有路径转发到 /。假设应用程序内的路径永远不会有以句点 (.) 开头的文件后缀(通常来自后端的所有真实静态文件都有这种后缀),这可以通过向 WebMvcConfigurer 添加一些内容来完成。

添加取决于哪种 模式SpringMVC中使用了匹配

路径模式匹配

使用路径模式匹配(这是最新 Spring Boot 中的新默认值):

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry
        .addViewController("/{path1:[\\w]+}")
        .setViewName("forward:/")
    registry
        .addViewController("/{path1}/{path2:[\\w]+}")
        .setViewName("forward:/")
    registry
        .addViewController("/{path1}/{path2}/{path3:[\\w]+}")
        .setViewName("forward:/")
}

使用路径模式匹配,没有简单的方法可以对任意数量的路径级别执行此操作因此,您必须为前端内部路径应支持的每一级嵌套添加这些语句之一。这是因为路径模式仅允许在模式末尾匹配多个路径级别 (**)。

上面最多支持三级所以直接在浏览器地址栏输入时:

/
/* / is served */

/a 
/* / is served which can then route internally to /a */

/a/b 
/* / is served which can then route internally to /a/b */

/a/b/c 
/* / is served which can then route internally to /a/b/c */

/a/b/c/d
/* will NOT work, tries to serve actual /a/b/c/d */

/a/b/c.txt 
/* / will NOT work, tries to serve actual /a/b/c.txt since contains a period */

这是怎么回事?如前所述,这些是路径模式,您可以阅读这里

  • 每个 {pathX} 匹配一个路径段,并将路径存储在变量 pathX 中(我们不这样做)不在乎)。
  • 每个变量在其匹配模式中必须有唯一的名称。
  • 在最后一个路径段中,我们匹配数字、字母和下划线的非空序列。如果您愿意,您可以修改此正则表达式,但您可以执行的操作非常有限。目标是不匹配任何带有句点的路径,因为这些可能是真正的静态文件。
  • 还有其他有用的路径模式,但它们可能不会与我们最终的正则表达式模式组合。

匹配

Ant 模式与 ant 模式 匹配,这曾经是默认的,适用类似的规则,但有一些差异。路径模式匹配通常更有效,但如前所述,它不允许您在最后的任何其他位置匹配任意数量的路径级别 (**)。

使用 ant 模式匹配,前面的配置可以简化为

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry
        .addViewController("/**/{path:[\\w]+}")
        .setViewName("forward:/")
}

现在,我们在最后一个路径之前匹配任意数量的路径级别,产生以下结果:

/
/* / is served */

/a 
/* / is served which can then route internally to /a */

/a/b 
/* / is served which can then route internally to /a/b */

/a/b/c 
/* / is served which can then route internally to /a/b/c */

/a/b/c/d
/* / is served which can then route internally to /a/b/c/d */

/a/b/c.txt 
/* / will NOT work, tries to serve actual /a/b/c.txt since contains a period */

因此,根据您应用程序其余部分的需要,我会选择以下之一这些解决方案,最好是带有蚂蚁图案的解决方案。

Mapping of static assets

Spring will automatically search in a number of places for paths which aren't matched by any controllers or other settings in the web config. These locations are currently checked by default:

classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/

You can override this behaviour by setting the

spring.web.resources.static-locations

application property.

Starting from a clean project and adding:

spring.web.resources.static-locations=classpath:/static/zanori2/

to your application.properties will make Spring search the intended path for static resources.

Mapping paths in single-page frontends to /

Remember in all of the below that the mapping to static assets has already been changed by the above so in the below, the path /a/b will REALLY fetch /static/zanori2/a/b. Also remember that a Spring controller will always have precedence over static assets so if you define a Spring controller which interferes with a static path, it will be used instead.

If you also support a single-page frontend with internal routing, you need to add some configuration to the WebMvcConfigurer. The trick is to still load all static content from its real location but to forward all the paths inside the single-page app to /. Assuming that paths inside the app will never have a file suffix preceded by a period (.), which typically all real static files from the backend have, this can be done by some additionss to your WebMvcConfigurer.

The additions depend on which kind of pattern matching is used in SpringMVC.

Path pattern matching

With path pattern matching (which is the new default in up-to-date Spring Boot):

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry
        .addViewController("/{path1:[\\w]+}")
        .setViewName("forward:/")
    registry
        .addViewController("/{path1}/{path2:[\\w]+}")
        .setViewName("forward:/")
    registry
        .addViewController("/{path1}/{path2}/{path3:[\\w]+}")
        .setViewName("forward:/")
}

With path pattern matching, there is no easy way to do this for an arbitrary number of path levels so you have to add one of these statements for every level of nesting your frontend internal paths should support. This is because path patterns only allow matching of multiple path levels (**) at the end of a pattern.

The above supports up to three levels so when directly entering in the browser address bar:

/
/* / is served */

/a 
/* / is served which can then route internally to /a */

/a/b 
/* / is served which can then route internally to /a/b */

/a/b/c 
/* / is served which can then route internally to /a/b/c */

/a/b/c/d
/* will NOT work, tries to serve actual /a/b/c/d */

/a/b/c.txt 
/* / will NOT work, tries to serve actual /a/b/c.txt since contains a period */

What's going on here? As said, these are path patterns which you can read about here:

  • Each {pathX} matches a path segment and stores the path in the variable pathX (which we don't care about).
  • Each variable must have a unique name within its matching pattern.
  • In the last path segment, we match a non-empty sequence of digits, letters and underscore. You can modify this regex if you want but it's quite restricted what you can do. The goal is to NOT match any paths with periods in them because these are likely real static files.
  • There are other path patterns which are useful but they may not be combined with our final regex pattern.

Ant pattern matching

With ant pattern matching, which used to be the default, similar rules apply but with some differences. Path pattern matching is generally more potent but as previously said, it does not allow you to match an arbitrary number of path levels (**) at no other place then at the end.

With ant pattern matching, the previous config can be simplified into

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry
        .addViewController("/**/{path:[\\w]+}")
        .setViewName("forward:/")
}

Now, we match an arbitrary number of path levels before the last path, yielding the following results:

/
/* / is served */

/a 
/* / is served which can then route internally to /a */

/a/b 
/* / is served which can then route internally to /a/b */

/a/b/c 
/* / is served which can then route internally to /a/b/c */

/a/b/c/d
/* / is served which can then route internally to /a/b/c/d */

/a/b/c.txt 
/* / will NOT work, tries to serve actual /a/b/c.txt since contains a period */

So depending on what you need in the rest of your app, I would choose one of these solutions, preferably the one with ant patterns.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文