3

I have a React client application which is build and compiled into the Java resource folder

src/main/resources/static

Content of the static folder is then served by standard Spring Boot application with no issues.

However, when I will start to use React Router, I need to be able to resolve this path:

localhost:8080/userSettingsPage

into index.html:

src/resource/static/index.html

I'm aware that I can do this in Controller as follow:

@Controller
public class MyController {
    @RequestMapping("/userSettingsPage")
    public String getIndex() {
       return "index.html";
    }
}

However I would like to specify my Controller in more generic way:

  1. If URL {path} ends with any of ".html", ".js", ".json", ".csv", ".css", ".png", ".svg", ".eot", ".ttf", ".woff", ".appcache", ".jpg", ".jpeg", ".gif", ".ico", then return file in /static/{path}
  2. Else return /static/index.html

How can I achieve it?

2
  • Does this answer your question stackoverflow.com/questions/39331929/… Commented Jun 29, 2021 at 12:41
  • Unfortunatelly it doesn't. Commented Jun 30, 2021 at 11:24

3 Answers 3

5

After lot of research and trying various approaches I have come to conclusion that the simplest solution would be to implement Filter and handle serving of static web files on my own, which bypasses the Spring's handling:

@Component
public class StaticContentFilter implements Filter {
    
    private List<String> fileExtensions = Arrays.asList("html", "js", "json", "csv", "css", "png", "svg", "eot", "ttf", "woff", "appcache", "jpg", "jpeg", "gif", "ico");
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }
    
    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String path = request.getServletPath();
        
        boolean isApi = path.startsWith("/api");
        boolean isResourceFile = !isApi && fileExtensions.stream().anyMatch(path::contains);
        
        if (isApi) {
            chain.doFilter(request, response);
        } else if (isResourceFile) {
            resourceToResponse("static" + path, response);
        } else {
            resourceToResponse("static/index.html", response);
        }
    }
    
    private void resourceToResponse(String resourcePath, HttpServletResponse response) throws IOException {
        InputStream inputStream = Thread.currentThread()
                .getContextClassLoader()
                .getResourceAsStream(resourcePath);
        
        if (inputStream == null) {
            response.sendError(NOT_FOUND.value(), NOT_FOUND.getReasonPhrase());
            return;
        }
        
        inputStream.transferTo(response.getOutputStream());
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

this works for me, thank you. However, the response is missing a Content Type header. If you don't specify the header, the index.html gets printed as text, not actually rendered. Also the header must be set correctly for .css, .js files.
4

Jurass's answer worked for me, but I needed to set ContentType headers. Without correct headers the browser would print the HTML as text in quotes. This is how I enabled natural paths in React.

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

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

@Component
public class StaticContentFilter implements Filter {

    private List<String> fileExtensions = Arrays.asList("html", "js", "json", "csv", "css", "png", "svg", "eot", "ttf", "woff", "appcache", "jpg", "jpeg", "gif", "ico");

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }

    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String path = request.getServletPath();

        boolean isApi = path.startsWith("/api");
        boolean isResourceFile = !isApi && fileExtensions.stream().anyMatch(path::contains);

        if (isApi) {
            chain.doFilter(request, response);
        } else if (isResourceFile) {
            resourceToResponse("static" + path, response);
        } else {
            resourceToResponse("static/index.html", response);
        }
    }

    private void resourceToResponse(String resourcePath, HttpServletResponse response) throws IOException {
        InputStream inputStream = Thread.currentThread()
                .getContextClassLoader()
                .getResourceAsStream(resourcePath);

        if (inputStream == null) {
            response.sendError(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.getReasonPhrase());
            return;
        }

        //headers
        if (resourcePath.endsWith(".html")) {
            response.setContentType("text/html");
        }
        if (resourcePath.endsWith(".css")) {
            response.setContentType("text/css");
        }
        if (resourcePath.endsWith(".js")) {
            response.setContentType("text/javascript");
        }

        inputStream.transferTo(response.getOutputStream());
    }
}

If you've found a better solution, please let me know.

Comments

0

I think what you're looking for is a ResourceHandler https://www.baeldung.com/spring-mvc-static-resources#3-chaining-resourceresolvers resp. https://github.com/eugenp/tutorials/blob/c9882f2090e918d0f441eeebf1f456e89dda2a17/spring-static-resources/src/main/java/com/baeldung/spring/MvcConfig.java#L57

Hope that helps you to have a starting point for your research :)

1 Comment

Unfortunately none of the provided solutions works, I have tried several of them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.