Angular URL 重写的嵌入式 Jetty
Embedded Jetty with Angular URL rewrite
我正在使用 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);
ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder
.create(AppEndpoint.class, "/wss-test")
.encoders(encoders)
.decoders(decoders)
.configurator(new AppEndpointConfig(config, factory))
.build();
container.addEndpoint(endpointConfig);
//server.setHandler(contextHandler);
HandlerList handlerList = new HandlerList();
handlerList.setHandlers(new Handler[]{rewriteHandler, contextHandler});
server.setHandler(handlerList);
contextHandler.addServlet(DefaultServlet.class, "/");
server.start();
// server.dump(System.err);
server.join();
我的 Angular 前端编译在应用程序的资源文件夹中,该文件夹由服务器提供 - 例如:localhost:8080/
。如果我的应用程序路由到 localhost:8080/some/path
一切都很好,但是当刷新页面时我得到一个 404 some/path
是未知的。使用 rewirte 规则(注释掉)我得到 ERR_TOO_MANY_REDIRECTIONS
也找不到延迟加载的资源/模块。关于如何为 Angular 应用程序使用 Jetty 重写的任何建议,因为我不知道如何在此处继续,或者 Jetty 不支持 angular deployment
建议的 apache 重写之类的东西
RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
在 Servlet 世界中实现此目的的唯一方法是使用错误页面处理 404 状态代码并自行发出重定向。
这只能在特定的 webapp 中完成,而不能通过通用的重写处理例程完成。
为什么?
嗯,"If the requested resource doesn't exist, use index.html" 是关键。
会发生什么。
- 已输入 webapp,但没有 url-模式匹配,因此 默认 url-模式(属于
"/"
)是用过。
- 默认的 url 模式映射到所谓的 "The Default Servlet"
- 默认 Servlet 负责在 webapp 的基础资源中查找匹配资源,并将该内容作为静态资源请求返回。
- 如果静态资源不存在,并且请求的是目录(例如:
/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
.
中查找为 404 声明的路径
<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-base
2019-09-24 14:17:55.540:INFO::main: Logging initialized @190ms to org.eclipse.jetty.util.log.StdErrLog
2019-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-b08
2019-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
这是由 ErrorHandling
servlet
处理的
$ 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
这是由 ErrorHandling
servlet
处理的
[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
这是由 ErrorHandling
servlet
处理的
对于正在为此寻找解决方案的任何人,我将其与 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)
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 WebSocket
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);
ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder
.create(AppEndpoint.class, "/" + wsPath)
.encoders(encoders)
.decoders(decoders)
.configurator(new AppEndpointConfig(config, factory))
.build();
container.addEndpoint(endpointConfig);
server.setHandler(rewriteHandler);
contextHandler.addServlet(DefaultServlet.class, "/");
server.start();
server.join();
它并不理想,因为只有特定的文件结尾与正则表达式匹配,但它确实适用于 Jetty 内构建和部署的 Angular 应用程序。如果使用 RESTful API,则必须定义一些正则表达式以匹配 API 的路径,例如我示例中的 wsPath
(一个简单的字符串)。
我正在使用 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);
ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder
.create(AppEndpoint.class, "/wss-test")
.encoders(encoders)
.decoders(decoders)
.configurator(new AppEndpointConfig(config, factory))
.build();
container.addEndpoint(endpointConfig);
//server.setHandler(contextHandler);
HandlerList handlerList = new HandlerList();
handlerList.setHandlers(new Handler[]{rewriteHandler, contextHandler});
server.setHandler(handlerList);
contextHandler.addServlet(DefaultServlet.class, "/");
server.start();
// server.dump(System.err);
server.join();
我的 Angular 前端编译在应用程序的资源文件夹中,该文件夹由服务器提供 - 例如:localhost:8080/
。如果我的应用程序路由到 localhost:8080/some/path
一切都很好,但是当刷新页面时我得到一个 404 some/path
是未知的。使用 rewirte 规则(注释掉)我得到 ERR_TOO_MANY_REDIRECTIONS
也找不到延迟加载的资源/模块。关于如何为 Angular 应用程序使用 Jetty 重写的任何建议,因为我不知道如何在此处继续,或者 Jetty 不支持 angular deployment
RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
在 Servlet 世界中实现此目的的唯一方法是使用错误页面处理 404 状态代码并自行发出重定向。
这只能在特定的 webapp 中完成,而不能通过通用的重写处理例程完成。
为什么?
嗯,"If the requested resource doesn't exist, use index.html" 是关键。
会发生什么。
- 已输入 webapp,但没有 url-模式匹配,因此 默认 url-模式(属于
"/"
)是用过。 - 默认的 url 模式映射到所谓的 "The Default Servlet"
- 默认 Servlet 负责在 webapp 的基础资源中查找匹配资源,并将该内容作为静态资源请求返回。
- 如果静态资源不存在,并且请求的是目录(例如:
/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-base
2019-09-24 14:17:55.540:INFO::main: Logging initialized @190ms to org.eclipse.jetty.util.log.StdErrLog
2019-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-b08
2019-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
这是由 ErrorHandling
servlet
$ 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
这是由 ErrorHandling
servlet
[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
这是由 ErrorHandling
servlet
对于正在为此寻找解决方案的任何人,我将其与 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)
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 WebSocket
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);
ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder
.create(AppEndpoint.class, "/" + wsPath)
.encoders(encoders)
.decoders(decoders)
.configurator(new AppEndpointConfig(config, factory))
.build();
container.addEndpoint(endpointConfig);
server.setHandler(rewriteHandler);
contextHandler.addServlet(DefaultServlet.class, "/");
server.start();
server.join();
它并不理想,因为只有特定的文件结尾与正则表达式匹配,但它确实适用于 Jetty 内构建和部署的 Angular 应用程序。如果使用 RESTful API,则必须定义一些正则表达式以匹配 API 的路径,例如我示例中的 wsPath
(一个简单的字符串)。