Supporting NextJS routing over Spring Boot app without any proxy

Viewed 405

I have created the build for a NextJS app to be served by a Spring Boot application. The root page / (which is index.html) opens fine, and from there NextJS handles client-side navigation by it's Links, so for example from the root / I can get to /user/edit(which is actually edit.html).

Everthing uptil now is fine. But now if the users decides the reload the page, or tries to open this page by typing/pasting the link. /user/edit will give a 404 as spring only identifies it as /user/edit.html.

I have tried

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //This works totally fine.
    registry.addResourceHandler("/**/*.css").addResourceLocations("classpath:static/");
    
    
    //This has not impact.
    registry.addResourceHandler("/**[^.]+$").addResourceLocations("classpath:static/**.html");
  }
}

I know the "classpath:static/**.html" doesn't make much sense to resolve. But if it's possible via this approach or anyother approach, that would be much appreciated. I just need to modify the request path of anything that's not from /api and doesn't have an extension to be forworded the the same + '.html'

I don't want to write my own controller to handle serving static content, also I have no SSR so woun't like to use Thymeleaf unless it's the last option, also woudn't want to change all my NextJS routes e.g. /user/edit.html -> /user/edit/index.html.

I've spent many hours searching and trying out different things, I believe Spring is open enough to have this somewhere as a configuration that I don't know of. Any help would be appreciated.

Thanks in advance.

1 Answers

Ok so I came up with a simple solution. Sharing so it could save time for others.

You need 3 files. Technically you can merge the 1st two into one, but just a bit more cleaner solution.

public abstract class PathForwardHandlerInterceptor implements HandlerInterceptor {

    abstract protected String provideAlternative(String path);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        final String alternative = provideAlternative(request.getServletPath());
        if (alternative != null) {
            request.getRequestDispatcher(alternative).forward(request, response);
            return false;
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}
public class ExtensionAppendInterceptor extends PathForwardHandlerInterceptor {
    @Override
    protected String provideAlternative(String path) {
        return ("/".equals(path) || path.contains(".")) ? null : path + ".html";
    }
}
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ExtensionAppendInterceptor());
    }

}
Related