Hi Joakim,

Thanks for trying to help. I appreciated it.

I tried using the error handler to trigger a rewrite of the URL but it
didn`t work well. Thanks to this, however, I learned that part of my
problem was that the WebappContext has an error handler enabled by default.
This gave me better understanding of what I had to do.

I want to share the solution with everyone in case other people want to use
Angular 6 applications on Jetty.

I created the following class which extends RewriteHandler and reads data
from a WebappContext to determine if the incoming target URL corresponds to
an existing file or api resource:

import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Html5PushStateConditionalRewriteHandler extends RewriteHandler
{


    private final WebAppContext webAppContext;

    public Html5PushStateConditionalRewriteHandler(WebAppContext
webAppContext) {
        this.webAppContext = webAppContext;
    }

    @Override
    public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response) throws
IOException, ServletException {

        if (isStarted()) {

            final MappedResource<ServletHolder> mappedServlet =
webAppContext.getServletHandler().getMappedServlet(target);

            boolean fileExists =
(mappedServlet.getResource().getServlet().getServletConfig().getServletContext().getResource(target)
!= null);


            boolean isDefaultServlet =
mappedServlet.getResource().getName().equals("default");


            // Do not interfere with urls that either :
            //   1) Map to an existing file or resource
            //   2) Fall under other servlet scopes (only default servlet
is targeted by this rewrite handler)
            if (isDefaultServlet && !fileExists) {

                // Apply rewrite rules
                super.handle(target, baseRequest, request, response);

            } else {

                // Pass along unchanged
                if (!baseRequest.isHandled()) {
                    Handler handler = _handler;
                    if (handler != null)
                        handler.handle(target, baseRequest, request,
response);
                }
            }
        }

    }




}



And then here is how it can be hooked up when building the embedded server:

// Create the web server
        server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(config.port);
        server.addConnector(connector);

        // Create the web app mainWebAppContext
        WebAppContext mainWebAppContext = new WebAppContext();
        mainWebAppContext.setContextPath("/");
        mainWebAppContext.setWelcomeFiles(new String[]{"index.html"});
        String resLocation =
WebServer.class.getResource("/webapp").toString();
        mainWebAppContext.setResourceBase(resLocation);

        // Enable URL Rewriting to support HTML 5 PushState
        RewriteHandler urlRewriteHandler = new
Html5PushStateConditionalRewriteHandler(mainWebAppContext);
        urlRewriteHandler.setRewriteRequestURI(true);
        urlRewriteHandler.setRewritePathInfo(false);
        urlRewriteHandler.setOriginalPathAttribute("requestedPath");

        RewriteRegexRule html5pushState = new RewriteRegexRule();
        html5pushState.setRegex("/.*");
        html5pushState.setReplacement("/index.html");
        urlRewriteHandler.addRule(html5pushState);

        [some code omitted]
mainWebAppContext.addServlet(websocketServletHolder, "/api/ws");

// Handler Structure: UrlRewriteHandler will filter URLs before they reach
the webapp context.
        urlRewriteHandler.setHandler(mainWebAppContext);
        server.setHandler(urlRewriteHandler);



Using this method, I have had excellent results. Better even than what
Tomcat and Ngnx do because this handler recognizes all servlet paths
automatically and does not rewrite them. Under tomcat we have to enter all
servlet paths manually in the rewrite.config file.

Hopefully this helps someone else.

*Nicolas Therrien*

Senior Software Developer

*[image:
https://www.motorolasolutions.com/content/dam/msi/images/logos/corporate/msiemailsignature.png]*

*o*: +1.819.931.2053


On Mon, Sep 10, 2018 at 9:35 PM Joakim Erdfelt <joa...@webtide.com> wrote:

> ErrorPageErrorHandler errorPageMapper = new ErrorPageErrorHandler();
> errorPageMapper.addErrorPage(404, "/myerror");
> context.setErrorHandler(errorPageMapper);
>
> context.addServlet(MyErrorServlet.class, "/myerror");
>
> Now you can handle any 404 error with your custom MyErrorServlet.class
>
> Pay attention to the DispatcherType, it should be ERROR.
> Pay attention to the HttpServletRequest.getAttributes() that have meaning
> in this state.
>
> See
> https://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html
> <https://urldefense.proofpoint.com/v2/url?u=https-3A__docs.oracle.com_javaee_7_api_javax_servlet_RequestDispatcher.html&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=D1kpDqrzg_vuhuc9UcfywK6jA4XmCli5HfL6vrf7zpA&e=>
> and all of the ERROR_* constants.
>
> Joakim Erdfelt / joa...@webtide.com
>
>
> On Mon, Sep 10, 2018 at 4:59 PM Nicolas Therrien <
> nicolas.therr...@motorolasolutions.com> wrote:
>
>> 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:
>> http://localhost:8080/mypage
>> <https://urldefense.proofpoint.com/v2/url?u=http-3A__localhost-3A8080_mypage&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=wxv6U6jkfXLrlygr0oi68Qj844IUh4t_kCIlXYiFZSs&e=>
>> Is a valid Angular route, but does not exist as a file.
>> I can navigate to it either via a link on the home page  (index.html) or
>> by typing: http://localhost:8080/#/mypage
>> <https://urldefense.proofpoint.com/v2/url?u=http-3A__localhost-3A8080_-23_mypage&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=RrPWclANmuoUpHv9BxIl1Nnydr1UeY7mKFe5HsNQjpE&e=>
>> in my browser.
>>
>> If I type http://localhost:8080/mypage
>> <https://urldefense.proofpoint.com/v2/url?u=http-3A__localhost-3A8080_mypage&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=wxv6U6jkfXLrlygr0oi68Qj844IUh4t_kCIlXYiFZSs&e=>,
>> I get a 404.
>>
>> 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:
>> https://angular.io/guide/router
>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__angular.io_guide_router&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=2ZoHdLbWQVoE6WohfEESuLTskCxsNAoD54Iff_A7hvw&e=>
>> https://angular.io/guide/deployment
>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__angular.io_guide_deployment&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=3F90xhwQtG5RGsFF66CruKKPvTZKCxlhV9wPq-5SM_o&e=>
>>
>> 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
>>
>> *[image:
>> https://www.motorolasolutions.com/content/dam/msi/images/logos/corporate/msiemailsignature.png]*
>>
>> *o*: +1.819.931.2053
>> _______________________________________________
>> jetty-users mailing list
>> jetty-users@eclipse.org
>> To change your delivery options, retrieve your password, or unsubscribe
>> from this list, visit
>> https://dev.eclipse.org/mailman/listinfo/jetty-users
>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__dev.eclipse.org_mailman_listinfo_jetty-2Dusers&d=DwMFaQ&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=XBMqXERb3CMzEhB3CxjIVZjiYbvYbH9drgozwYmWKh4&e=>
>
> _______________________________________________
> jetty-users mailing list
> jetty-users@eclipse.org
> To change your delivery options, retrieve your password, or unsubscribe
> from this list, visit
>
> https://urldefense.proofpoint.com/v2/url?u=https-3A__dev.eclipse.org_mailman_listinfo_jetty-2Dusers&d=DwICAg&c=q3cDpHe1hF8lXU5EFjNM_A&r=P3_1pTtMQK06fFymYIWbyyzVU6nc0CcwfuZhLhexammvaiCaU0ieHeI7BWvfbbjE&m=5VHcVIOmMAA3s1Gy9YhxJU_BQjnRsqmR5bwFHo6k_pU&s=XBMqXERb3CMzEhB3CxjIVZjiYbvYbH9drgozwYmWKh4&e=
_______________________________________________
jetty-users mailing list
jetty-users@eclipse.org
To change your delivery options, retrieve your password, or unsubscribe from 
this list, visit
https://dev.eclipse.org/mailman/listinfo/jetty-users

Reply via email to