JSP在Spring Boot应用中始终显示404错误,无论我做什么。

huangapple go评论77阅读模式
英文:

JSP in Spring Boot application yields 404 Error no matter what I do

问题

我首先要说明,我已经查看并尝试了关于这个问题的每一个问题中的解决方案。最大的问题是,大多数这些解决方案都非常陈旧,在过去的几年里,Spring Boot 已经发生了很大变化。明确地说,我已经尝试过 这个这个这个这个,还有更多。我也阅读了许多教程。但都没有奏效。

我有一个全新的 Spring Boot 应用程序,我正试图让 JSP 渲染工作起来。这是我的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
    </dependency>

    <!-- 其他依赖... -->
</dependencies>

我的项目结构如下:

- source
  - production
    - java
    - resources
      - WEB-APP
        - jsp
          - initialization
            - begin.jsp

我知道父级目录有些不太标准,但在 Maven 中已经正确配置,我已经确认这不是问题的根源。

我的 Application 类如下:

@SpringBootApplication(scanBasePackages="com.my.project")
public class Application {
    // ... 省略部分代码 ...
    
    @Bean
    public ViewResolver viewResolver() {
        // ... 省略部分代码 ...
    }
    
    public static void main(final String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

我的控制器:

@Controller
public class InitializationController {
    // ... 省略部分代码 ...
    
    @GetMapping("/initialize_application")
    public String beginInitialization(ModelMap model) {
        // ... 省略部分代码 ...
        return "initialization/begin";
    }
}

但当我访问 /initialize_application 时,出现了 404 错误。我确认控制器是正常工作的,但 JSP 无法找到。

其他尝试:

  • 最初我没有在应用程序上使用 @EnableWebMvc。没有它时,日志中只有我的日志语句。当我添加了 @EnableWebMvc 之后,会记录下这个 404 错误:No mapping for GET /WEB-APP/jsp/initialization/begin.jsp(或者其他我尝试过的目录,除了 "WEB-APP")。
  • 我尝试了在命令行中直接使用 mvn spring-boot:run 运行。
  • 我尝试了在 IntelliJ IDEA 中使用 Maven 运行配置和命令 spring-boot:run(结果相同)。
  • 我尝试了 &lt;packaging&gt;jar&lt;/packaging&gt;&lt;packaging&gt;war&lt;/packaging&gt;,但都没有区别,因为既不会创建 JAR 也不会创建 WAR。Maven 直接从 target/classes 目录运行应用程序。
  • 当我尝试将 WEB-INFMETA-INF 改为 WEB-APPwebapp 时,会看到一个警告:Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]

我确认了我的 JSP 存在于 target/classes/WEB-APP/jsp(或者其他我尝试过的目录,除了 "WEB-APP"),所以它们确实存在。

现在我感到困惑不解。我开始觉得我需要放弃 Spring Boot,改用传统的 Spring Web MVC 应用程序,使用 Servlet 配置和 Tomcat 安装,但我真的很喜欢 Spring Boot 的 "just runs" 特性。感谢任何帮助。

更新 1

在阅读了关于 JSP 限制的这部分 Spring 文档 之后,我现在知道我必须使用 &lt;packaging&gt;war&lt;/packaging&gt;,我正在使用它,但没有任何改变。我开始怀疑根本问题是,maven spring-boot:run 不会创建 WAR 并运行它,它只是将一切构建到 target/classes 并从那里运行。

此外,在找到了这个旧的、官方的 Spring Boot 示例应用程序之后,我对项目结构进行了一些更改:

- source
  - production
    - java
    - resources
    - webapp
      - META-INF
      - WEB-INF
      - jsp
        - initialization
          - begin.jsp

我更新了视图解析器的配置:

resolver.setPrefix("/jsp/");
resolver.setSuffix(".jsp");

并在我的 POM 中添加了以下内容:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>3.3.1</version>
    <configuration>
        <warSourceDirectory>source/production/webapp</warSourceDirectory>
    </configuration>
</plugin>

如果我运行 mvn package,我的 WAR 会被正确创建(类和 JSP 都在应该的位置),但 mvn spring-boot:runmvn package spring-boot:run 都不起作用——我仍然得到 404 错误,无法解析我的 JSP。

以上是您提供的内容的翻译。如果您有其他需要翻译的内容或有任何问题,请随时告诉我。

英文:

I'll start off by saying I've looked at and tried the solutions in every question regarding this that I can find. The biggest problem is that most of these solutions are very old, and Spring Boot has changed a lot in the last several years. To be clear, I've tried this, this, this, this, and more. I've also read numerous tutorials. Nothing works.

I have a brand new Spring Boot application and I'm trying to get JSP rendering working with it. These are my dependencies:

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;version&gt;[2.3.4.RELEASE,3)&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
        &lt;version&gt;[2.3.4.RELEASE,3)&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-validation&lt;/artifactId&gt;
        &lt;version&gt;[2.3.4.RELEASE,3)&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
        &lt;artifactId&gt;jstl&lt;/artifactId&gt;
        &lt;version&gt;1.2&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;commons-io&lt;/groupId&gt;
        &lt;artifactId&gt;commons-io&lt;/artifactId&gt;
        &lt;version&gt;[2.8.0,3)&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;de.mkammerer&lt;/groupId&gt;
        &lt;artifactId&gt;argon2-jvm&lt;/artifactId&gt;
        &lt;version&gt;[2.7,3)&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;mysql&lt;/groupId&gt;
        &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
        &lt;version&gt;[8.0.21,9)&lt;/version&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.glassfish.jaxb&lt;/groupId&gt;
        &lt;artifactId&gt;jaxb-runtime&lt;/artifactId&gt;
        &lt;version&gt;[2.3.2,)&lt;/version&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;
        &lt;version&gt;[2.3.4.RELEASE,3)&lt;/version&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.apache.tomcat.embed&lt;/groupId&gt;
        &lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt;
        &lt;version&gt;[9.0.38,)&lt;/version&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
        &lt;version&gt;[2.3.4.RELEASE,3)&lt;/version&gt;
        &lt;scope&gt;test&lt;/scope&gt;
        &lt;exclusions&gt;
            &lt;exclusion&gt;
                &lt;groupId&gt;org.junit.vintage&lt;/groupId&gt;
                &lt;artifactId&gt;junit-vintage-engine&lt;/artifactId&gt;
            &lt;/exclusion&gt;
        &lt;/exclusions&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

My project is laid out as follows:

- source
  - production
    - java
      - [my source code packages]
    - resources
      - WEB-APP
        - jsp
          - initialization
            - begin.jsp
      - [my resource packages]
  - test
    - java
    - resources

"WEB-APP/jsp" is just the latest iteration I've tried. I've tried "WEB-INF/jsp", "META-INF/jsp", "webapp/jsp", no parent (just "jsp"), etc., all with the same results.

I know the parent directories are a bit non-standard, but it's configured correctly in Maven and I've confirmed it's not the source of my problems:

&lt;build&gt;
    &lt;sourceDirectory&gt;source/production/java&lt;/sourceDirectory&gt;
    &lt;resources&gt;
        &lt;resource&gt;
            &lt;directory&gt;source/production/resources&lt;/directory&gt;
        &lt;/resource&gt;
    &lt;/resources&gt;
    ...
&lt;/build&gt;

My Application class is as follows:

@SpringBootApplication(scanBasePackages=&quot;com.my.project&quot;)
@EnableWebMvc
@EnableJpaRepositories(&quot;com.my.project.repository&quot;)
@EntityScan(&quot;com.my.project.model&quot;)
public class Application
{
    private static final Logger LOGGER = LogManager.getLogger(Application.class);

    public Application()
    {
    }

    @Bean
    public ViewResolver viewResolver()
    {
        LOGGER.info(&quot;Constructing InternalResourceViewResolver[JstlView]&quot;);
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix(&quot;/WEB-APP/jsp/&quot;);
        resolver.setSuffix(&quot;.jsp&quot;);
        resolver.setViewClass(JstlView.class);
        resolver.setRedirectContextRelative(true);
        resolver.setRedirectHttp10Compatible(false);
        return resolver;
    }

    public static void main(final String[] args)
    {
        SpringApplication.run(Application.class, args);
    }
}

And my Controller:

@Controller
public class InitializationController
{
    private static final Logger LOGGER = LogManager.getLogger(InitializationController.class);

    @GetMapping(&quot;/initialize_application&quot;)
    public String beginInitialization(ModelMap model)
    {
        LOGGER.info(&quot;Beginning initialization&quot;);
        ...
        LOGGER.info(&quot;Returning view&quot;);
        return &quot;initialization/begin&quot;;
    }
    ...
}

On startup I see the "Constructing InternalResourceViewResolver" log entry (my view resolver bean is created). When I go to /initialize_application, I get the following error:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Oct 18 21:45:26 CDT 2020
There was an unexpected error (type=Not Found, status=404).

Looking in the log again, I see "Beginning initialization" and "Returning view," so I know that the 404 is for my JSP and not my controller. My controller is working.

Other things I've tried:

  • Initially I did not have @EnableWebMvc on my application. Without it, the log was empty except my log statements. When I added @EnableWebMvc, this is now logged with the 404: No mapping for GET /WEB-APP/jsp/initialization/begin.jsp (or whatever other directory I've tried other than "WEB-APP").
  • I've tried running this directly on the pure command line with mvn spring-boot:run
  • I've tried running this in IntelliJ IDEA with a Maven run configuration and command spring-boot:run (same result)
  • I've tried both &lt;packaging&gt;jar&lt;/packaging&gt; and &lt;packaging&gt;war&lt;/packaging&gt;, but neither make a difference, because neither a JAR nor a WAR are ever made. Maven runs the application directly out of the target/classes directory instead of creating an artifact.
  • When I've tried WEB-INF or META-INF instead of WEB-APP or webapp or something else, I've seen a logged warning: Path with &quot;WEB-INF&quot; or &quot;META-INF&quot;: [WEB-INF/jsp/initialization/begin.jsp]

I have also confirmed that my JSPs are present in target/classes/WEB-APP/jsp (or whatever other directory I've tried other than "WEB-APP"), so they do exist.

I'm at a loss how to proceed. I'm beginning to think I need to ditch Spring Boot and stick with a traditional boilerplate Spring Web MVC application with a Servlet config and a Tomcat installation, but I was really excited about the "just runs" aspect of Spring Boot. Any help would be appreciated.

UPDATE 1

After reading this Spring documentation about JSP limitations, I now know that I have to use &lt;packaging&gt;war&lt;/packaging&gt;, and I'm using that now, but it hasn't made a difference. I'm starting to suspect that the underlying problem here is that maven spring-boot:run doesn't create a WAR and run it, it just builds everything to target/classes and runs it from there.

Also, after finding this old, official Spring boot samples application, I've changed my project structure a little:

- source
  - production
    - java
      - [my source code packages]
    - resources
      - [my resource packages]
    - webapp
      - META-INF
      - WEB-INF
      - jsp
        - initialization
          - begin.jsp
  - test
    - java
    - resources

Updated my view resolver configuration:

resolver.setPrefix(&quot;/jsp/&quot;);
resolver.setSuffix(&quot;.jsp&quot;);

And added this to my POM:

        &lt;plugin&gt;
            &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
            &lt;version&gt;3.3.1&lt;/version&gt;
            &lt;configuration&gt;
                &lt;warSourceDirectory&gt;source/production/webapp&lt;/warSourceDirectory&gt;
            &lt;/configuration&gt;
        &lt;/plugin&gt;

If I run mvn package, my WAR gets created correctly (classes and JSPs all where they should be), but neither mvn spring-boot:run nor mvn package spring-boot:run work—I still get 404 errors resolving my JSPs.

The old Spring Boot sample application linked to above puts the JSPs in WEB-INF/jsp, but I can't do that, because that results in the warning Path with &quot;WEB-INF&quot; or &quot;META-INF&quot;: [WEB-INF/jsp/initialization/begin.jsp] (and still 404). What's frustrating is that this sample application doesn't exist anymore, nor does any new variation of it. I can't find any updated version that works with the newest version of Spring Boot. The sample application was deleted in 2.2.x.

答案1

得分: 2

你可以尝试将 tomcat-embed-jasper 的范围更改为 provided,因为需要这个依赖来编译 JSP。

    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>[9.0.38,)</version>
        <scope>provided</scope>
    </dependency>

Edit:

我在互联网上寻找了各种 spring-boot + jsp 项目。我注意到它们还使用了 spring-boot-starter-tomcat,并将范围设置为 provided。你可以尝试如下配置:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>[2.3.4.RELEASE,3)</version>
        <scope>provided</scope>
    </dependency>

参考链接:

https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

https://dzone.com/articles/spring-boot-2-with-jsp-view

Edit-2:

这次我创建了一个新的 Spring Boot 项目,并进行了最少的设置以渲染 JSP。基本上,我遵循了这个 教程,我的项目可以正常运行。

然后,我将 pom.xml 替换为你提供的内容,然后我遇到了你在问题中提到的相同错误。

然后,我尝试了一些试错操作,我将 &lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt; 中的 &lt;version&gt;[9.0.38,)&lt;/version&gt; 移除后,问题得到解决。

        <!--我在这里移除了版本信息,然后问题得到解决-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <!--<version>[9.0.38,)</version>-->
            <scope>provided</scope>
        </dependency>

尽管我的目录结构可能有所不同,但正如你所提到的,这不是问题的原因。

我已经将项目上传到了 GitHub。你可以随意拉取并在本地运行。

Github

英文:

Can you try by changing the scope of tomcat-embed-jasper to provided as this dependency is needed to compile JSPs.

    &lt;dependency&gt;
        &lt;groupId&gt;org.apache.tomcat.embed&lt;/groupId&gt;
        &lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt;
        &lt;version&gt;[9.0.38,)&lt;/version&gt;
        &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;

Edit:

I looked for various spring-boot + jsp projects over internet. I noticed that they have they also have spring-boot-starter-tomcat with provided scope. Can you try this.

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;
        &lt;version&gt;[2.3.4.RELEASE,3)&lt;/version&gt;
        &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;

References :

https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/

https://dzone.com/articles/spring-boot-2-with-jsp-view

Edit-2 :

So this time i created a new springboot project. Did bare minimum setup to get jsp rendered. So basically i followed this tutorial and my project was running fine.

Then I replaced the pom.xml with yours and the i got the same error you mentioned in the question.

Then while doing trial and error i removed the &lt;version&gt;[9.0.38,)&lt;/version&gt; from &lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt; and it started working for me.

        &lt;!--I have removed version here and it started working for me--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.tomcat.embed&lt;/groupId&gt;
            &lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt;
&lt;!--            &lt;version&gt;[9.0.38,)&lt;/version&gt;--&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;

Although i have different directory structure. But as you mentioned that is not the cause of issue.

I have uploaded the project to github. Feel free to pull it run it locally.

Github

答案2

得分: -1

假设您的网页内容位于以下位置(这应该在类路径之外),source/production/webapp。由于在命令行或IDE中运行时DocumentRoot中的硬编码路径用于检测目录,Spring Boot将忽略此路径(在构建war并运行该war时可以工作)。

作为解决方法,您可以将TomcatContextCustomizer添加为bean以检测路径并将其设置为正确的基础路径。

package com.my.project;

@SpringBootApplication
public class Application extends SpringBootServletInitializer 
{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
    {
        return application.sources(DemoApplication.class);
    }

    public static void main(final String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public TomcatContextCustomizer docBaseCustomizer() 
    {
        return new TomcatContextCustomizer() 
        {
            public void customize(Context context) 
            {
                File root = new File("source/production/webapp");
                if (root.exists() && root.isDirectory()) 
                {
                    context.setDocBase(root.getAbsolutePath());
                }        
            }
        };
    } 
}

现在将以下内容添加到您的application.properties

spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp

注意: 只有当@SpringBootApplication注解的类位于com.my.project包中时,才能删除其他注解。然后它会自动检测其他类(如实体和存储库)。

英文:

Assuming the following location for your web content (which should be outside the classpath AFAIK) source/production/webapp. Spring Boot will ignore this due to a hardcoded path in DocumentRoot for detection of directories when running from the command-line or IDE (it will work when building a war and running that).

As a workaround you can add a TomcatContextCustomizer as a bean to detect the path and set it as the correct base.

package com.my.project;

@SpringBootApplication
public class Application extends SpringBootServletInitializer 
{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
    {
        return application.sources(DemoApplication.class);
    }

    public static void main(final String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public TomcatContextCustomizer docBaseCustomizer() 
    {
        return new TomcatContextCustomizer() 
        {
            public void customize(Context context) 
            {
                File root = new File(&quot;source/production/webapp&quot;);
                if (root.exists() &amp;&amp; root.isDirectory()) 
                {
                    context.setDocBase(root.getAbsolutePath());
                }        
            }
        }
    } 
}

Now add the following to your application.properties

spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp

NOTE: The removal of the other annotations can only be done if your @SpringBootApplication annotated class is in the com.my.project package. It will then automatically detect the other classes (like entities and repositories).

huangapple
  • 本文由 发表于 2020年10月19日 11:17:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/64420693.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定