如何使用 Grizzly 在同一基础 url 提供静态内容和资源

How to serve static content and resource at same base url with Grizzly

我正在使用 Grizzly 来提供我的 REST 服务,它可以有多个 "modules"。我希望能够对服务和静态内容使用相同的基础 URL,这样我就可以访问所有这些网址:

我尝试设置的代码如下所示:

private HttpServer createServer(String host, int port, ResourceConfig config)
{               
    HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create("http://" + host + ":" + port + "/"), config, false);

    HttpHandler httpHandler = new CLStaticHttpHandler(HttpServer.class.getClassLoader(), "docs/");
    server.getServerConfiguration().addHttpHandler(httpHandler, "/");

    return server;
}

使用此代码,我只能看到 html 页面,并且在我尝试获取资源时得到 "Resource identified by path does not exist" 响应。 当我注释掉添加 HttpHandler 的代码时,我就可以访问我的资源(但当然没有文档)。 我需要做什么才能同时访问我的资源和静态内容?

您可以做的一件事是 运行 Grizzly 作为 servlet 容器。这样您就可以 run Jersey as servlet filter, and add a default servlet 处理静态内容。例如

public class Main {
    public static HttpServer createServer() {
        WebappContext context = new WebappContext("GrizzlyContext", "");
        createJerseyFilter(context);
        createDefaultServlet(context);
        HttpServer server = GrizzlyHttpServerFactory
                .createHttpServer(URI.create("http://localhost:8080/"));
        context.deploy(server);
        return server;
    }

    private static void createJerseyFilter(WebappContext context) {
        ResourceConfig rc = new ResourceConfig().packages("com.grizzly.test");
        // This causes Jersey to forward 404s to default servlet
        // which will catch all the static content requests.
        rc.property(ServletProperties.FILTER_FORWARD_ON_404, true);
        FilterRegistration reg = context.addFilter("JerseyApp", new ServletContainer(rc));
        reg.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), "/*");
    }

    private static void createDefaultServlet(WebappContext context) {
        ArraySet<File> baseDir = new ArraySet<>(File.class);
        baseDir.add(new File("."));
        ServletRegistration defaultServletReg 
                = context.addServlet("DefaultServlet", new DefaultServlet(baseDir) {});
        defaultServletReg.addMapping("/*");
    }

    public static void main(String[] args) throws IOException {
        HttpServer server = createServer();      
        System.in.read();
        server.stop();
    }
}

您将需要添加 Jersey Grizzly servlet 依赖项

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-grizzly2-servlet</artifactId>
    <version>${jersey2.version}</version>
</dependency>

这种方法的唯一问题是默认的 servlet 是为了从文件系统提供文件,而不是像您当前尝试做的那样从类路径提供文件。您可以在 createDefaultServlet 方法中看到我只是将基本目录设置为当前工作目录。所以这就是您所有文件都需要的地方。您可以将其更改为 "docs",这样您的所有文件都将位于 docs 文件夹中,该文件夹将位于当前工作目录中。

如果您想从类路径中读取文件,您可能需要实现自己的 servlet。您可以查看 source code for DefaultServlet and try to modify it to serve from the classpath. You can also check out Dropwizard's AssetServlet,它已经提供类路径中的内容。

或者您可以直接说算了,只从文件系统提供服务:-)

我最终自己编写了一个服务来处理静态资源。我决定从文件系统提供我的文件,但这种方法也适用于从 jar 提供文件 - 您只需要将文件作为资源获取,而不是直接创建文件。

@Path("/")
public class StaticService
{

    @GET
    @Path("/{docPath:.*}.{ext}")
    public Response getHtml(@PathParam("docPath") String docPath, @PathParam("ext") String ext, @HeaderParam("accept") String accept)
    {
        File file = new File(cleanDocPath(docPath) + "." + ext);
        return Response.ok(file).build();
    }

    @GET
    @Path("{docPath:.*}")
    public Response getFolder(@PathParam("docPath") String docPath)
    {
        File file = null;
        if ("".equals(docPath) || "/".equals(docPath))
        {
            file = new File("index.html");
        }
        else
        {
            file = new File(cleanDocPath(docPath) + "/index.html"); 
        }
        return Response.ok(file).build();
    }

    private String cleanDocPath(String docPath)
    {
        if (docPath.startsWith("/"))
        {
            return docPath.substring(1);
        }
        else
        {
            return docPath;
        }
    }
}