skip to Main Content

We have a web application in which the backend is spring boot and front end is react. We are trying to integrate these applications in such a way that we can make use of spring boot jar file to run them both on the same port.

Following piece of java code is being used for integration.

@Bean
    public RouterFunction<ServerResponse> htmlRouter(@Value("classpath:/static/index.html") Resource html) {
        return RouterFunctions.route(
                GET("/*"),
                request -> ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(html)
        );
    }

This is working fine when we create frontend build using react-scripts build. We want to make use of webpack build as there will be multiple such applications and they will be integrated using micro front-end which will work only when webpack is in place.
When trying do so the jar file is getting built. When we run this jar file and hit the url we get following error-

refused to execute script from 'http://localhost:8765/abc/xyz' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.

Part of webpack config

output: {
        path: path.join(__dirname, 'build'),
        publicPath: '/',
        filename: 'bundle.js',
    }

Following is the folder structure of the project

enter image description here

2

Answers


  1. Your router is configured to handle all URLs and return an HTML page:

    return RouterFunctions.route(
        GET("/*"),
        request -> ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(html)
    );
    

    If your page contains a <script src="..."> tag, the browser will fetch the URL to get the script.
    The server will return an HTML page with the text/html content-type, which triggers the error message
    because the browser expects text/javascript instead.

    Login or Signup to reply.
  2. You may try using Spring’s script views for this purpose: if you think about that, in the end you need some type of engine for dealing with SSR. For React, the documentation suggest the use of Nashorn.

    The documentation suggest a possible configuration for different scripting libraries:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.scriptTemplate();
        }
    
        @Bean
        public ScriptTemplateConfigurer configurer() {
            ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
            configurer.setEngineName("nashorn");
            configurer.setScripts("mustache.js");
            configurer.setRenderObject("Mustache");
            configurer.setRenderFunction("render");
            return configurer;
        }
    }
    

    In the specific use case of React we can find different, more complete examples, for instance in this Github repository:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import org.springframework.web.servlet.view.script.ScriptTemplateConfigurer;
    
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
      @Override
      public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
        viewResolverRegistry.scriptTemplate().prefix("/templates/").suffix(".html");
      }
    
      @Bean
      public ScriptTemplateConfigurer scriptTemplateConfigurer() {
        ScriptTemplateConfigurer scriptTemplateConfigurer = new ScriptTemplateConfigurer("nashorn");
        scriptTemplateConfigurer.setScripts("/js/polyfill.js", "/js/built/bundle-server.js");
        scriptTemplateConfigurer.setRenderFunction("render");
        scriptTemplateConfigurer.setSharedEngine(false);
        return scriptTemplateConfigurer;
      }
    
    }
    

    Or this other one, dated, but I think still useful as well:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.view.script.ScriptTemplateConfigurer;
    import org.springframework.web.servlet.view.script.ScriptTemplateViewResolver;
    
    /**
     * @author David Hancock
     */
    @Configuration
    public class SSRConfiguration {
    
        @Bean
        public ViewResolver reactViewResolver() {
            return new ScriptTemplateViewResolver("/public/", ".html");
        }
    
        @Bean
        public ScriptTemplateConfigurer reactConfigurer() {
            ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
            configurer.setEngineName("nashorn");
            configurer.setScripts(
                    "public/nashorn-polyfill.js",
                    "public/server.js"
            );
            configurer.setRenderFunction("render");
            configurer.setSharedEngine(false);
            return configurer;
        }
    
    }
    

    As you can see they look quite similar in the implementation: basically, you need to indicate the scripts that should be loaded by the script engine, one of course the bundle generated by webpack, and at least one more (see this or this other example), a polyfill for supporting window, etc.

    I came across another good example: it is for Vue, not for React, but even in that case it provides a good example of using the Nashorn engine for SSR.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search