具有 Angular URL 重写功能的嵌入式 Jetty

我正在使用 Jetty 部署 WebSocket 和 Angular 应用程序。在开发中,一切正常,但在生产中,我遇到了一个问题,当刷新前端或输入 url 时,我从服务器收到 404 消息,指出给定的资源不存在。现在我正在尝试创建重写规则以将请求重定向到我的index.html。初始化我的服务器如下所示:


server = new Server();

ServerConnector connector = new ServerConnector(server);

connector.setPort(config.getServer().getPort());

server.addConnector(connector);


RewriteHandler rewriteHandler = new RewriteHandler();

rewriteHandler.setRewriteRequestURI(true);

rewriteHandler.setRewritePathInfo(false);

rewriteHandler.setOriginalPathAttribute("requestedPath");


/* 

RedirectRegexRule rule1 = new RedirectRegexRule();

rule1.setRegex("/(.+)");

rule1.setLocation("/index.html");


rewriteHandler.addRule(rule1);

*/


URL webRootLocation = this.getClass().getResource("/frontend/index.html");

if (webRootLocation == null)

    throw new IllegalStateException("Unable to determine webroot URL location");


URI webRootUri = URI.create(webRootLocation.toURI().toASCIIString().replaceFirst("/index.html$","/"));

logger.debug("Web Root URI: {}",webRootUri);


ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

contextHandler.setContextPath("/");

contextHandler.setBaseResource(Resource.newResource(webRootUri));

contextHandler.setWelcomeFiles(new String[]{ "index.html" });


rewriteHandler.setHandler(contextHandler);


ServerContainer container = WebSocketServerContainerInitializer.initialize(contextHandler);


List<Class<? extends Encoder>> encoders = new ArrayList<>();

encoders.add(MessageEncoder.class);

List<Class<? extends Decoder>> decoders = new ArrayList<>();

decoders.add(MessageDecoder.class);

我的 Angular 前端编译在应用程序的资源文件夹中,该文件夹由服务器提供服务 - 例如:localhost:8080/。如果我的应用程序路由到localhost:8080/some/path一切都很好,但刷新页面时我会收到some/path未知的 404。使用重写规则(注释掉)我ERR_TOO_MANY_REDIRECTIONS也得到了延迟加载的资源/模块未找到。关于如何对 Angular 应用程序使用 Jetty 重写的任何建议,因为我不知道如何继续这里,或者 Jetty 不支持Angular 部署建议的 apache 重写之类的东西



波斯汪
浏览 159回答 2
2回答

慕哥6287543

对于任何正在寻找解决方案的人,我让它与 Jettys 一起工作RewriteHandler。我的服务器设置现在如下所示:server = new Server(config.getServer().getPort());RewriteHandler rewriteHandler = new RewriteHandler();rewriteHandler.setRewriteRequestURI(true);rewriteHandler.setRewritePathInfo(false);rewriteHandler.setOriginalPathAttribute("requestedPath");RewriteRegexRule rule1 = new RewriteRegexRule();rule1.setRegex("^((?!"+wsPath+"|\\.js|\\.css|\\.jpe?g|\\.png).)*$");rule1.setReplacement("/index.html");rewriteHandler.addRule(rule1);URL webRootLocation = this.getClass().getResource("/frontend/index.html");if (webRootLocation == null)&nbsp; &nbsp; throw new IllegalStateException("Unable to determine Web-Root URL location");URI webRootUri = URI.create(webRootLocation.toURI().toASCIIString().replaceFirst("/index.html$","/"));logger.debug("Web-Root URI: {}",webRootUri);ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);contextHandler.setContextPath("/");contextHandler.setBaseResource(Resource.newResource(webRootUri));contextHandler.setWelcomeFiles(new String[]{ "index.html" });rewriteHandler.setHandler(contextHandler);// Initialize WebSocketServerContainer container = WebSocketServerContainerInitializer.initialize(contextHandler);List<Class<? extends Encoder>> encoders = new ArrayList<>();encoders.add(MessageEncoder.class);List<Class<? extends Decoder>> decoders = new ArrayList<>();decoders.add(MessageDecoder.class);ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder&nbsp; &nbsp; &nbsp; &nbsp; .create(AppEndpoint.class, "/" + wsPath)&nbsp; &nbsp; &nbsp; &nbsp; .encoders(encoders)&nbsp; &nbsp; &nbsp; &nbsp; .decoders(decoders)&nbsp; &nbsp; &nbsp; &nbsp; .configurator(new AppEndpointConfig(config, factory))&nbsp; &nbsp; &nbsp; &nbsp; .build();container.addEndpoint(endpointConfig);server.setHandler(rewriteHandler);contextHandler.addServlet(DefaultServlet.class, "/");server.start();server.join();这并不理想,因为只有特定的文件结尾与正则表达式匹配,但它确实可以与 Jetty 内构建和部署的 Angular 应用程序配合使用。如果使用 RESTful API,则必须定义一些正则表达式来匹配 API 的路径,如wsPath我的示例中的(一个简单的字符串)。

沧海一幻觉

在 Servlet 世界中实现此目的的唯一方法是让错误页面处理 404 状态代码并自行发出重定向。这只能在特定的 Web 应用程序中完成,而不能从通用重写处理例程中完成。为什么?那么,“如果请求的资源不存在,则使用index.html”是关键。会发生什么。输入了 web 应用程序,但没有匹配的 url-pattern,因此使用默认的 url-pattern (of )。"/"默认 url 模式映射到所谓的“默认 Servlet”默认 Servlet 负责在 Web 应用程序的基础资源中查找匹配的资源,并将该内容作为静态资源请求返回。如果静态资源不存在,并且请求是针对目录(例如:/js/然后使用欢迎文件列表来查找欢迎文件。(此列表在 中配置WEB-INF/web.xml)<welcome-file-list>    <welcome-file>index.html</welcome-file>    <welcome-file>index.htm</welcome-file>    <welcome-file>index.jsp</welcome-file></welcome-file-list>如果静态资源仍然不存在,则将其作为 404 响应进行处理。同时,servlet 规范的错误页面处理例程启动。这将导致在您的WEB-INF/web.xml.<servlet>  <servletname>404Handler</servlet-name>  <servlet-class>com.acme.My404Servlet</servlet-class></servlet><servlet-mapping>  <servlet-name>404Handler</servlet-name>  <url-pattern>/404status</url-pattern></servlet-mapping><!-- ... then later ... --><error-page>    <error-code>404</error-code>    <location>/404status</location></error-page>该路径可以是 servlet、静态资源、jsp 等。几乎可以是任何可以通过路径引用的内容。如果它是 servlet(或 jsp),您可以通过请求属性询问原始请求,以了解处理此错误的原因。嵌入式码头中的一个例子是......package jetty.errors;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import javax.servlet.DispatcherType;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.eclipse.jetty.server.Server;import org.eclipse.jetty.server.handler.DefaultHandler;import org.eclipse.jetty.server.handler.HandlerList;import org.eclipse.jetty.servlet.DefaultServlet;import org.eclipse.jetty.servlet.ErrorPageErrorHandler;import org.eclipse.jetty.servlet.ServletContextHandler;import org.eclipse.jetty.servlet.ServletHolder;import org.eclipse.jetty.util.resource.PathResource;public class EmbeddedWelcomeErrorDemo{    public static void main(String[] args) throws Exception    {        Server server = new Server(8080);        String baseDir = System.getProperty("user.home");        if (args.length > 0)            baseDir = args[0];        Path basePath = Paths.get(baseDir);        if (!Files.exists(basePath) || !Files.isDirectory(basePath))        {            throw new IOException("Not a valid directory: " + basePath);        }        ServletContextHandler context = new ServletContextHandler();        context.setContextPath("/");        context.setBaseResource(new PathResource(basePath));        context.setWelcomeFiles(new String[]{            "index.html"        });        // Add error page mapping for context        context.addServlet(ErrorHandling.class, "/errorpage");        ErrorPageErrorHandler errorMapper = new ErrorPageErrorHandler();        errorMapper.addErrorPage(404, "/errorpage");        context.setErrorHandler(errorMapper);        // to handle static resources against base resource (always last)        // always named "default" (per spec)        ServletHolder defaultHolder = new ServletHolder("default", DefaultServlet.class);        // assigned to default url-pattern of "/" (per spec)        context.addServlet(defaultHolder, "/");        HandlerList handlers = new HandlerList();        handlers.addHandler(context);        handlers.addHandler(new DefaultHandler()); // for non-context errors        server.setHandler(handlers);        server.start();        server.join();    }    public static class ErrorHandling extends HttpServlet    {        @Override        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException        {            if (req.getDispatcherType() != DispatcherType.ERROR)            {                // we didn't get here from a error dispatch.                // somebody attempted to use this servlet directly.                resp.setStatus(404);                return;            }            String requestedResource = (String)req.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);            log("[ErrorHandling] Requested resource was " + requestedResource);            int statusCode = (int)req.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);            switch (statusCode)            {                case 404:                    // let handle it by a redirect                    resp.sendRedirect("/");                    break;                default:                    // pass the other errors through                    resp.setStatus(statusCode);                    break;            }        }    }}发生的情况的一些例子。$ mkdir $HOME/tmp-base$ mdkir css$ echo "this is the index.html" > index.html$ echo "this is my other html" > myother.html$ echo "this is my fancy css" > css/main.css然后使用命令行运行服务器示例到该目录$ java ... jetty.errors.EmbeddedWelcomeErrorDemo $HOME/tmp-base2019-09-24 14:17:55.540:INFO::main: Logging initialized @190ms to org.eclipse.jetty.util.log.StdErrLog2019-09-24 14:17:55.621:INFO:oejs.Server:main: jetty-9.4.20.v20190813; built: 2019-08-13T21:28:18.144Z; git: 84700530e645e812b336747464d6fbbf370c9a20; jvm 1.8.0_202-b082019-09-24 14:17:55.661:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@7921b0a2{/,file:///home/joakim/tmp-base/,AVAILABLE}2019-09-24 14:17:55.674:INFO:oejs.AbstractConnector:main: Started ServerConnector@7cef4e59{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}2019-09-24 14:17:55.674:INFO:oejs.Server:main: Started @325ms然后提出一些要求...$ curl -L -vv http://localhost:8080/*   Trying ::1...* TCP_NODELAY set* Connected to localhost (::1) port 8080 (#0)> GET / HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Date: Tue, 24 Sep 2019 19:26:28 GMT< Last-Modified: Tue, 24 Sep 2019 19:12:21 GMT< Content-Type: text/html< Accept-Ranges: bytes< Content-Length: 23< Server: Jetty(9.4.20.v20190813)< this is the index.html* Connection #0 to host localhost left intact这是受欢迎的文件处理$ curl -L -vv http://localhost:8080/myother.html*   Trying ::1...* TCP_NODELAY set* Connected to localhost (::1) port 8080 (#0)> GET /myother.html HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Date: Tue, 24 Sep 2019 19:21:10 GMT< Last-Modified: Tue, 24 Sep 2019 19:13:46 GMT< Content-Type: text/html< Accept-Ranges: bytes< Content-Length: 22< Server: Jetty(9.4.20.v20190813)< This is my other html* Connection #0 to host localhost left intact这是正常的静态文件服务$ curl -L -vv http://localhost:8080/css/main.css*   Trying ::1...* TCP_NODELAY set* Connected to localhost (::1) port 8080 (#0)> GET /css/main.css HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Date: Tue, 24 Sep 2019 19:22:22 GMT< Last-Modified: Tue, 24 Sep 2019 19:22:16 GMT< Content-Type: text/css< Accept-Ranges: bytes< Content-Length: 21< Server: Jetty(9.4.20.v20190813)< this is my fancy css* Connection #0 to host localhost left intact这是正常的静态文件服务如果我向不存在的资源或目录发出一些请求......$ curl -L -vv http://localhost:8080/css/bogus.css*   Trying ::1...* TCP_NODELAY set* Connected to localhost (::1) port 8080 (#0)> GET /css/bogus.css HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 302 Found< Date: Tue, 24 Sep 2019 19:22:46 GMT< Location: http://localhost:8080/< Content-Length: 0< Server: Jetty(9.4.20.v20190813)< * Connection #0 to host localhost left intact* Issue another request to this URL: 'http://localhost:8080/'* Found bundle for host localhost: 0x5647e1581a50 [can pipeline]* Re-using existing connection! (#0) with host localhost* Connected to localhost (::1) port 8080 (#0)> GET / HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Date: Tue, 24 Sep 2019 19:22:46 GMT< Last-Modified: Tue, 24 Sep 2019 19:12:21 GMT< Content-Type: text/html< Accept-Ranges: bytes< Content-Length: 23< Server: Jetty(9.4.20.v20190813)< this is the index.html* Connection #0 to host localhost left intact这是由ErrorHandlingservlet处理的$ curl -L -vv http://localhost:8080/this/directory/does/not/exist*   Trying ::1...* TCP_NODELAY set* Connected to localhost (::1) port 8080 (#0)> GET /this/directory/does/not/exist HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 302 Found< Date: Tue, 24 Sep 2019 19:23:02 GMT< Location: http://localhost:8080/< Content-Length: 0< Server: Jetty(9.4.20.v20190813)< * Connection #0 to host localhost left intact* Issue another request to this URL: 'http://localhost:8080/'* Found bundle for host localhost: 0x561eefa8b020 [can pipeline]* Re-using existing connection! (#0) with host localhost* Connected to localhost (::1) port 8080 (#0)> GET / HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Date: Tue, 24 Sep 2019 19:23:02 GMT< Last-Modified: Tue, 24 Sep 2019 19:12:21 GMT< Content-Type: text/html< Accept-Ranges: bytes< Content-Length: 23< Server: Jetty(9.4.20.v20190813)< this is the index.html* Connection #0 to host localhost left intact这是由ErrorHandlingservlet处理的[joakim@hyperion tmp]$ curl -L -vv http://localhost:8080/non-existant.jpeg*   Trying ::1...* TCP_NODELAY set* Connected to localhost (::1) port 8080 (#0)> GET /non-existant.jpeg HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 302 Found< Date: Tue, 24 Sep 2019 19:21:18 GMT< Location: http://localhost:8080/< Content-Length: 0< Server: Jetty(9.4.20.v20190813)< * Connection #0 to host localhost left intact* Issue another request to this URL: 'http://localhost:8080/'* Found bundle for host localhost: 0x563f476b6a50 [can pipeline]* Re-using existing connection! (#0) with host localhost* Connected to localhost (::1) port 8080 (#0)> GET / HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Date: Tue, 24 Sep 2019 19:21:18 GMT< Last-Modified: Tue, 24 Sep 2019 19:12:21 GMT< Content-Type: text/html< Accept-Ranges: bytes< Content-Length: 23< Server: Jetty(9.4.20.v20190813)< this is the index.html* Connection #0 to host localhost left intact
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java