【问题标题】:Serve favicon.ico and other static files with VertX使用 VertX 提供 favicon.ico 和其他静态文件
【发布时间】:2017-03-04 23:45:12
【问题描述】:

我正在尝试提供一个网站图标和一些字体。

object Lion : AbstractVerticle() {

    @JvmStatic
    @Throws(IOException::class)
    fun main(args: Array<String>) {

    val vertx = Vertx.vertx()
    val router = Router.router(vertx)

    router.route().handler(CorsHandler.create("*")
        .allowedMethod(HttpMethod.GET)
        .allowedMethod(HttpMethod.POST)
        .allowedMethod(HttpMethod.OPTIONS)
        .allowedHeader("X-PINGARUNER")
        .allowedHeader("Content-Type"))

    // some json GET / POST routes here

    router.route().handler(FaviconHandler.create());
    router.route().handler(StaticHandler.create())

    vertx.createHttpServer().requestHandler { router.accept(it) }.listen(9090)
}

当我转到http://localhost:9090/favicon.ico 时,FaviconHandler 引发异常,导致“内部服务器错误” 我的图标位于src/main/resources/webroot/favicon.ico

Oct 22, 2016 11:16:42 PM io.vertx.ext.web.impl.RoutingContextImplBase
SEVERE: Unexpected exception in route
java.lang.RuntimeException: java.lang.NullPointerException
    at io.vertx.ext.web.handler.impl.FaviconHandlerImpl.init(FaviconHandlerImpl.java:148)
    at io.vertx.ext.web.handler.impl.FaviconHandlerImpl.handle(FaviconHandlerImpl.java:155)
    at io.vertx.ext.web.handler.impl.FaviconHandlerImpl.handle(FaviconHandlerImpl.java:33)
    at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:215)
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78)
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94)
    at io.vertx.ext.web.handler.impl.CorsHandlerImpl.handle(CorsHandlerImpl.java:121)
    at io.vertx.ext.web.handler.impl.CorsHandlerImpl.handle(CorsHandlerImpl.java:38)
    at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:215)
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78)
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94)
    at io.vertx.ext.web.impl.RouterImpl.accept(RouterImpl.java:79)
    at Lion$main$8.handle(Lion.kt:90)
    at Lion$main$8.handle(Lion.kt:43)
    at io.vertx.core.http.impl.ServerConnection.handleRequest(ServerConnection.java:286)
    at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:412)
    at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:139)
    at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.lambda$createConnAndHandle$1(HttpServerImpl.java:712)
    at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:314)
    at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:190)
    at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.createConnAndHandle(HttpServerImpl.java:706)
    at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:570)
    at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:522)
    at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:76)
    at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:122)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350)
    at io.vertx.core.http.impl.HttpServerImpl$Http1xOrHttp2Handler.http1(HttpServerImpl.java:1019)
    at io.vertx.core.http.impl.HttpServerImpl$Http1xOrHttp2Handler.channelRead(HttpServerImpl.java:990)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:610)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:551)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:465)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:437)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
    at io.vertx.ext.web.handler.impl.FaviconHandlerImpl$Icon.<init>(FaviconHandlerImpl.java:61)
    at io.vertx.ext.web.handler.impl.FaviconHandlerImpl$Icon.<init>(FaviconHandlerImpl.java:40)
    at io.vertx.ext.web.handler.impl.FaviconHandlerImpl.init(FaviconHandlerImpl.java:143)
    ... 48 more

删除 FaviconHandler 和正常的 htmljscss 文件可以正常使用,但字体无法使用。

Failed to decode downloaded font: http://localhost:9090/fonts/glyphicons-halflings-regular.woff
/#/tcr:1 OTS parsing error: incorrect file size in WOFF header

当我直接访问该字体网址时,浏览器正尝试将字体下载为常规文件。

这似乎是一个潜在的解决方案,但我看到的不是我的图标,只是一个 16x16 的正方形,里面有扭曲的线条,字体仍在尝试下载,但仍然在浏览器控制台中显示错误。

    router.route("/favicon.ico").handler {
        it.response().putHeader("Content-Type", "image/x-icon").sendFile("webroot/favicon.ico")
    }

    router.route("/fonts/glyphicons-halflings-regular.woff").handler { 
         it.response().putHeader("Content-Type", "application/font-woff").sendFile("webroot/fonts/glyphicons-halflings-regular.woff")
    }

总而言之,我如何使 StaticHandler 使用正确的 MimeTypes 正确地服务 .woff.ico 文件而不让它下载这些文件?

解决方案

我遇到的第一个问题是 maven shade 出于某种原因没有将所有字体文件复制到 jar 中。

在构建时显式复制字体文件并禁用字体资源过滤似乎已经解决了字体问题。

<build>
    <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
    <resources>
        <resource>
            <directory>${basedir}/src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>${basedir}/src/main/resources/fonts</directory>
            <filtering>false</filtering>
        </resource>
    </resources>

然后从资源目录而不是字体目录提供字体修复它(VertX 出于某种原因不会从字体目录提供服务,即使文件在那里)

   router.route("/fonts/glyphicons-halflings-regular.woff2").handler {
            it.response().sendFile("glyphicons-halflings-regular.woff2")
        }
        router.route("/fonts/glyphicons-halflings-regular.woff").handler {
            it.response().sendFile("glyphicons-halflings-regular.woff")
        }
        router.route("/fonts/glyphicons-halflings-regular.ttf").handler {
            it.response().sendFile("glyphicons-halflings-regular.ttf")
        }
        router.route().handler(StaticHandler.create().setCachingEnabled(true));

Favicon 可能正在被缓存,仍在调查中,目前修复 Favicon 并不那么重要。

router.route("/favicon.ico").handler(FaviconHandler.create("favicon.ico"))

【问题讨论】:

    标签: kotlin vert.x


    【解决方案1】:

    Favicon.ico:

    只需将您的favicon.ico 文件上一级直接移动到resources 文件夹中:

    /src/main/resources/favicon.ico
    

    或者更改您的FaviconHandler 以传递路径和文件名:

    router.route().handler(FaviconHandler.create(FaviconHandler.create("webroot/favicon.ico")))
    

    您的代码假定FaviconHandler 也尊重StaticHandler 中设置的webroot,但事实并非如此。这纯粹是StaticHandler 的属性,因此FaviconHandlerresources/ 中查找资源,除非您指定相对路径。当找不到该资源时,it uses the result of getResourceAsStream("favicon.ico")null 并崩溃。

    如果您查看FaviconHandlerImpl 的单元测试,您会看到they place the favicon.ico file in the root of resources 而不是webroot


    WOFF 文件:

    至于你的字体文件,你在错误的地方寻找问题。它与 MIME 类型无关。

    更有可能是您意外损坏了 WOFF 文件。当您使用 GIT 提交它们并且它认为它们是文本文件并且它破坏了行结尾从而损坏它们时,可能会发生这种情况。或者你使用了 Maven 过滤器插件,它也做了同样的事情,破坏了它们。或者您通过 FTP 将它们作为文本文件上传/下载,同样的问题。

    查看其他帖子:https://stackoverflow.com/a/33792610/3679676

    【讨论】:

    • Maven 确实应该为此负责,首先 maven shade 出于某种原因没有复制所有字体文件(只有一半),资源过滤也破坏了它,即使在我得到它复制时,VertX 仍然没有从字体目录提供服务,因此将字体复制到资源修复了它。在我的问题中查看我更新的解决方案。
    【解决方案2】:

    您没有指定您的网站图标路径,因此 Vertx 会尝试找到默认路径:

     if (path == null) {
        icon = new Icon(Utils.readResourceToBuffer("favicon.ico"));
     }
    

    您的网站图标位于 resources/webroot/favicon.ico,但 Vertx 在 resources/favicon.ico 查找它

    因此您可以指定FaviconHandler.create("webroot/favicon.ico") 或将其上移一个目录。

    关于 WOFF 文件,我无法重现问题,因为 WOFF 返回 application/x-font-woff,这似乎是正确的。

    【讨论】:

    • 你在我之后 5 分钟发布了与我相同的答案。
    • 对不起,杰森,我花了一些时间来发布它。不过,我认为没有理由生气,因为我还提供了一种无需将其移动到任何地方即可提供网站图标的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 2011-11-10
    • 1970-01-01
    • 2020-01-29
    • 2010-10-18
    • 2014-11-07
    相关资源
    最近更新 更多