0

I have been struggling to get my react app running over spring boot server. On running the spring server, I am able to see contents on localhost:8080 however spring boot is not able to serve any static files /about /login etc. I get the whitelabel Error Page 404 error .

When I run the react app standalone on npm, I am able to route without any problem.

I have followed the tutorials mentioned here but simplified it for my purposes.

The structure of my project looks like

src
   main
       java
           App.java
       resources
                application.propperties
       webapp
             src
                /components
                           NavMain.js
                /pages
                      AboutPage.js
                      WelcomePage.js
                /helpers
                app.js       

This is what my App.JS looks like

<div>
  <NavMain />
  <BrowserRouter>
    <Routes>
      <Route exact path="/" element={<WelcomePage />} />
      <Route exact path="/about" element={<AboutPage />} />
    </Routes>
  </BrowserRouter>
</div>

I am serving the react files in spring by copying files into static folder pom.xml

<execution>
  <id>copy-resources</id>
  <!-- here the phase you need -->
  <phase>process-resources</phase>
  <goals>
    <goal>copy-resources</goal>
  </goals>
  <configuration>
    <outputDirectory>
     ${basedir}/target/classes/static
    </outputDirectory>
    <resources>
      <resource>
        <directory>
          src/main/webapp/build
        </directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </configuration>
</execution>

I tried

  1. HashRouter instead of BrowserRouter but does not work
  2. Add a @controller java file to route URL but did not work (React-Router issues when serving React App with Spring Boot)
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
ksernow
  • 662
  • 3
  • 14
  • 33

1 Answers1

0

What worked for me is

package hype;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.ResourceResolverChain;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * Redirects every page to index.html
 * Used to handle the router
 */
@Configuration
public class SinglePageAppConfig implements WebMvcConfigurer {

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**")
      .addResourceLocations("classpath:/static/")
      .resourceChain(false)
      .addResolver(new PushStateResourceResolver());
  }

  private class PushStateResourceResolver implements ResourceResolver {
    private Resource index = new ClassPathResource("/static/index.html");
    private List<String> handledExtensions = Arrays.asList("html", "js", "json", "csv", "css", "png", "svg", "eot", "ttf", "woff", "appcache", "jpg", "jpeg", "gif", "ico");
    private List<String> ignoredPaths = Arrays.asList("api");

    @Override
    public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
      return resolve(requestPath, locations);
    }

    @Override
    public String resolveUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain) {
      Resource resolvedResource = resolve(resourcePath, locations);
      if (resolvedResource == null) {
        return null;
      }
      try {
        return resolvedResource.getURL().toString();
      } catch (IOException e) {
        return resolvedResource.getFilename();
      }
    }

    private Resource resolve(String requestPath, List<? extends Resource> locations) {
      if (isIgnored(requestPath)) {
        return null;
      }
      if (isHandled(requestPath)) {
        return locations.stream()
          .map(loc -> createRelative(loc, requestPath))
          .filter(resource -> resource != null && resource.exists())
          .findFirst()
          .orElseGet(null);
      }
      return index;
    }

    private Resource createRelative(Resource resource, String relativePath) {
      try {
        return resource.createRelative(relativePath);
      } catch (IOException e) {
        return null;
      }
    }

    private boolean isIgnored(String path) {
      return ignoredPaths.contains(path);
    }

    private boolean isHandled(String path) {
      String extension = StringUtils.getFilenameExtension(path);
      return handledExtensions.stream().anyMatch(ext -> ext.equals(extension));
    }
  }
}

ksernow
  • 662
  • 3
  • 14
  • 33