I'm building a Quarkus app with a React frontend. I'm very new to react and I'm currently trying to deploy my app. The problem is that once I build my react app and try to serve it with Quarkus on port 8080, fetching the "/" path works fine but entering "/contact" for example ends up on a 404 error page. I know there are some solutions to this problem already on the internet but every one of them that I found and tried did not work. The problem seems to coming from react-router-dom that I use in the frontend app and which does not work well with Quarkus as this shows. So i'm trying implement the "Catch-all" solution of the previous post. The solution which seems to be the more promising to me would be this one but on my side the errorHandler is never triggered when I get a 404 error :
@ApplicationScoped
public class RouterReactFix {
public void init(@Observes Router router) {
log.info("Initialized !"); // Triggered
router.errorHandler(404, rc -> {
log.info("404"); // Not triggered
rc.reroute("/");
});
router.errorHandler(200, rc -> {
log.info("200"); // Not triggered, but it isn't an error code so maybe not relevant
});
router.get("/my-route").handler(rc -> {log.info("TRIGGERED");}); // Triggered
}
}
I could really use your help either to make this work with my react app or to find another working solution. Thank your in advance.
Edit :
What I currently have as a "setup" :
In a "AppHandler.tsx" file :
<Switch>
<Route exact path={'/'} component={HomePage} />
<Route path={'/presentation'} component={PresentationPage} />
<Route path={'/qanda'} component={QAndAPage} />
<Route path={'/bots'} component={BotTabHandler} />
<Route path={'/rules'} component={Rules} />
<Route path={'/contact'} component={ContactPage} />
</Switch>
The usual way to navigate through my website is to click on buttons which are embedded in components like this one :
<Link to={buttonElement.page}> // where buttonElement.page has for example the value '/presentation'
Now if I click on the buttons ( components) of the page in the built version (the version served in a static way by the Quarkus server) all works fine, the url in the browser changes to (for example) localhost:port/presentation and my page is displayed correctly. However, if on the same setup, still on the built version, I enter manually in my browser the url "localhost:port/presentation" I get a 404 not found error from Quarkus. This error doesn't appear when the website is still served by React on port 3000 and other requests (server-related, such as DB calls) are proxied by React on port 8080 to be handled by Quarkus (I get no 404 error when I enter manually the URL into the browser search bar)
Given that the Vert.x router
configuration is not working, then I would assume there is a higher-level component which configuration is superseding the low-level Vert.x configuration.
Typically in these cases, I would guess you are using a RESTful resource provider implementation which typically would be Resteasy for Quarkus.
Resteasy provides other JAX-RS compliant means for handling exceptions mapped to REST resources where the 404 HTTP resource not found error would map to the javax.ws.rs.NotFoundException
exception.
To handle such an exception having it redirect to your web application root path, you would implement an javax.ws.rs.ext.ExceptionMapper
that may look as follows:
@Provider
@Priority(Priorities.USER)
public class CustomNotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
@Override
public Response toResponse(NotFoundException e) {
System.out.println("Handling " + e.getMessage());
return Response.temporaryRedirect(URI.create("/")).build();
}
}
Note that the @Priority(Priorities.USER)
annotation needs to be added to override the default Quarkus mapper.
In case you would like to preserve the URL path so that the client side framework (React / React Router in your case) would display the appropriate content accordingly, you can stream the main index.html
page at each unmatched request:
@Provider
@Priority(Priorities.USER)
public class MyNotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
@Override
public Response toResponse(NotFoundException e) {
final InputStream indexIS = this.getClass().getClassLoader().getResourceAsStream("/META-INF/resources/index.html");
final InputStreamReader isr = new InputStreamReader(indexIS, StandardCharsets.UTF_8);
final StringBuilder sb = new StringBuilder();
int c;
try {
while ((c = isr.read()) != -1) {
sb.append((char) c);
}
} catch (IOException ex) {
return Response.temporaryRedirect(URI.create("/")).build();
}
return Response.ok(sb.toString(), MediaType.TEXT_HTML_TYPE).build();
}
}