使用 Dropwizard 提供多个静态资产
Serving multiple static assets using Dropwizard
我有一个正在构建并尝试使用 AssetBundle 提供服务的 React 应用程序,因此:
@Override
public void initialize(final Bootstrap<PersonalWebsiteConfiguration> bootstrap) {
bootstrap.addBundle(new SwaggerBundle<PersonalWebsiteConfiguration>() {
protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(final PersonalWebsiteConfiguration configuration) {
return configuration.swaggerBundleConfiguration;
}
});
bootstrap.addBundle(new AssetsBundle("/build", "/", "index.html"));
}
我也添加了配置
server:
rootPath: /api
所以不会和我的 API.
发生冲突
这仅适用于我的 React 应用程序的登录页面。每当我尝试路由 /login /dashboard 时,都找不到来自 UI 的页面。所以我尝试添加更多包来解决路由问题:
bootstrap.addBundle(new AssetsBundle("/build", "/", "index.html"));
bootstrap.addBundle(new AssetsBundle("/build", "/login", "index.html"));
bootstrap.addBundle(new AssetsBundle("/build", "/dashboard", "index.html"));
现在,只有仪表板在工作。有谁知道如何使用多个 routing/pages.
服务 React 构建
对于单页应用程序,您需要每个客户端路由到 return index.html(以支持浏览器重新加载或登陆 / 以外的路径)
据我所知,Dropwizard AssetBundle 无法做到这一点,即使用 index.html 为所有路由提供服务。
查看相似(旧)question.
您可以自己实现一个 servlet 过滤器或使用一些社区插件,例如 this one。
我必须说另一种方法对我来说效果更好,根本不要使用 dropwizard 来提供静态资产,只将它用作后端 API。
为 API 和静态资产使用 CDN 路由或不同的子域。这样你就可以在 www.mydomain.com 处拥有静态资产,在 api.mydomain.com 处拥有 API (或使用相同的域并基于路径前缀,例如 /api 路由到后端或静态资源)
您可以添加过滤器来实现该功能。
对于实现这种过滤器的 dropwizard 插件,请参阅 https://github.com/xvik/dropwizard-guicey-ext/tree/master/guicey-spa。
以下独立示例代码使用 Kotlin。
class SinglePageAppFilter : Filter {
override fun doFilter(servletRequest: ServletRequest,
servletResponse: ServletResponse,
chain: FilterChain) {
val request = servletRequest as HttpServletRequest
val response = servletResponse as HttpServletResponse
if (request.requestURI == "/" || request.requestURI.startsWith("/api")) {
chain.doFilter(servletRequest, servletResponse)
} else {
val wrapper = ResponseWrapper(response)
chain.doFilter(servletRequest, wrapper)
val sc = wrapper.sc
if (sc == HttpServletResponse.SC_NOT_FOUND) {
request.getRequestDispatcher("/").forward(request, response)
} else if (sc != null) {
response.sendError(sc)
}
}
}
override fun init(filterConfig: FilterConfig) {}
override fun destroy() {}
}
class ResponseWrapper(response: HttpServletResponse) : HttpServletResponseWrapper(response) {
var sc: Int? = null
override fun sendError(sc: Int) {
this.sc = sc
}
}
class MyApplication : Application<MyConfiguration>() {
override fun initialize(bootstrap: Bootstrap<MyConfiguration>) {
bootstrap.addBundle(AssetsBundle("/assets", "/", "index.html"))
}
override fun run(configuration: MyConfiguration, environment: Environment) {
environment.servlets().addFilter("SinglePageAppFilter", SinglePageAppFilter())
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*")
}
}
我有一个正在构建并尝试使用 AssetBundle 提供服务的 React 应用程序,因此:
@Override
public void initialize(final Bootstrap<PersonalWebsiteConfiguration> bootstrap) {
bootstrap.addBundle(new SwaggerBundle<PersonalWebsiteConfiguration>() {
protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(final PersonalWebsiteConfiguration configuration) {
return configuration.swaggerBundleConfiguration;
}
});
bootstrap.addBundle(new AssetsBundle("/build", "/", "index.html"));
}
我也添加了配置
server:
rootPath: /api
所以不会和我的 API.
发生冲突这仅适用于我的 React 应用程序的登录页面。每当我尝试路由 /login /dashboard 时,都找不到来自 UI 的页面。所以我尝试添加更多包来解决路由问题:
bootstrap.addBundle(new AssetsBundle("/build", "/", "index.html"));
bootstrap.addBundle(new AssetsBundle("/build", "/login", "index.html"));
bootstrap.addBundle(new AssetsBundle("/build", "/dashboard", "index.html"));
现在,只有仪表板在工作。有谁知道如何使用多个 routing/pages.
服务 React 构建对于单页应用程序,您需要每个客户端路由到 return index.html(以支持浏览器重新加载或登陆 / 以外的路径) 据我所知,Dropwizard AssetBundle 无法做到这一点,即使用 index.html 为所有路由提供服务。 查看相似(旧)question.
您可以自己实现一个 servlet 过滤器或使用一些社区插件,例如 this one。
我必须说另一种方法对我来说效果更好,根本不要使用 dropwizard 来提供静态资产,只将它用作后端 API。 为 API 和静态资产使用 CDN 路由或不同的子域。这样你就可以在 www.mydomain.com 处拥有静态资产,在 api.mydomain.com 处拥有 API (或使用相同的域并基于路径前缀,例如 /api 路由到后端或静态资源)
您可以添加过滤器来实现该功能。 对于实现这种过滤器的 dropwizard 插件,请参阅 https://github.com/xvik/dropwizard-guicey-ext/tree/master/guicey-spa。 以下独立示例代码使用 Kotlin。
class SinglePageAppFilter : Filter {
override fun doFilter(servletRequest: ServletRequest,
servletResponse: ServletResponse,
chain: FilterChain) {
val request = servletRequest as HttpServletRequest
val response = servletResponse as HttpServletResponse
if (request.requestURI == "/" || request.requestURI.startsWith("/api")) {
chain.doFilter(servletRequest, servletResponse)
} else {
val wrapper = ResponseWrapper(response)
chain.doFilter(servletRequest, wrapper)
val sc = wrapper.sc
if (sc == HttpServletResponse.SC_NOT_FOUND) {
request.getRequestDispatcher("/").forward(request, response)
} else if (sc != null) {
response.sendError(sc)
}
}
}
override fun init(filterConfig: FilterConfig) {}
override fun destroy() {}
}
class ResponseWrapper(response: HttpServletResponse) : HttpServletResponseWrapper(response) {
var sc: Int? = null
override fun sendError(sc: Int) {
this.sc = sc
}
}
class MyApplication : Application<MyConfiguration>() {
override fun initialize(bootstrap: Bootstrap<MyConfiguration>) {
bootstrap.addBundle(AssetsBundle("/assets", "/", "index.html"))
}
override fun run(configuration: MyConfiguration, environment: Environment) {
environment.servlets().addFilter("SinglePageAppFilter", SinglePageAppFilter())
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*")
}
}