Hi,
I want to add an Angular 6 application to an existing embedded Jetty web server (version 9.4.12.v20180830). I am having a problem getting client-side page routing to work properly.
For example:
Is a valid Angular route, but does not exist as a file.
That is because for client-side routing feature to work properly, HTML 5 PushState must be supported. From my understanding, what this means is that the server must rewrite URLs so that they reach the angular page on /index.html. Using the above example, "/mypage" would have to be rewritten to "/index.html" with an original path attribute set to the requested path.
Here is are links to Angular documentation:
I've been working at it for a few days and am stuck. I have found how to do url rewriting with Jetty, using a RewriteHandler. The rewriting works, but is affecting all URLs, including valid ones. This causes script files and images to be replaced with contents of index.html.
I need URLs associated with other servlets (such as websocket servlets and rest apis) to be left untouched, and I need URLs for real files (such as images and other angular assets) to be left untouched.
I have been looking for something in the like of Ngnx:
try_files $uri $uri/ /index.html;
Which translates into "try the URI as-is, if that doesn`t work, treat the URI as a folder, if that doesnt work then use index.html"
So I tried this with Jetty:
// Create the web app context
WebAppContext context = new WebAppContext();
context.setContextPath("/");
context.setWelcomeFiles(new String[] {"index.html"});
String resLocation = WebServer.class.getResource("/webapp").toString();
context.setResourceBase(resLocation);
// Enable URL Rewriting to support HTML 5 PushState
RewriteHandler rewrite = new RewriteHandler();
rewrite.setRewriteRequestURI(true);
rewrite.setRewritePathInfo(false);
rewrite.setOriginalPathAttribute("requestedPath");
RewriteRegexRule html5pushState = new RewriteRegexRule();
html5pushState.setRegex("/.*");
html5pushState.setReplacement("/index.html");
rewrite.addRule(html5pushState);
// Handler Structure
HandlerList handlers = new HandlerList();
rewrite.setHandler(context);
handlers.setHandlers(new Handler[] { context, rewrite});
server.setHandler(handlers);
I was hoping this would do the trick. My rationale was that using a HandlerList would cause Jetty to call each handler in the list in the order specified until a response is matched. Calling context first would lookup all the existing webapps for a file or servlet. Then calling rewrite would rewrite the URL and then because context is also a child of rewrite I was hoping this would cause Jetty to try again using the rewritten URL, in which case would be index.html.
Unfortunately, It doesn`t work because "context" returns a 404 to the client directly. The rewrite handler is never called.
I've been looking for Conditional statements in Jetty and couldn`t find any. In fact, I was able to do this kind of conditional rewrite with Tomcat using a RewriteValve:
ctx.addValve(new RewriteValve());
and a text file (rewrite.config):
RewriteCond %{REQUEST_URI} -f
RewriteRule ^(.*)$ - [L]
RewriteRule ^(.*)$ /index.html
Notice how the rewrite rule is conditional. The rewrite occurs only if the URL cannot be matched to a file.
How can this kind of conditional rewriting be done under Jetty?
If a custom handler must be written, how would you guys query the server to figure out if a given path matches a servlet or a file?
Your help would be very much appreciated.
Nicolas Therrien
Senior Software Developer
o: +1.819.931.2053