使用 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, "/*")
    }

}