This should be an easy one, but it's not been so far. I've been working with vert.x 2
for a bit and switched to vert.x 3
recently. I thought I'd try a simple vertx-web example but can't get past a simple serving up of static files.
My server class contains the following snippets:
HttpServer server = vertx.createHttpServer();
Router router = ...;
router.route("/static/*").handler(StaticHandler.create().setCachingEnabled(false));
server.requestHandler(router::accept).listen(ctx.port);
I'm using Eclipse, but have also been trying running vertx
from the the command line. I'm also using Maven. I have three webroot folders, and vert.x
can find none of them:
myproject/webroot
myproject/src/main/resources/webroot
myproject/src/main/java/webroot
Each of those 'webroot's contains an index.html
, and a css/base.css
file.
The first one is in my project's root folder. The second is in the Maven resources folder, and the third should be flat-out on my classpath
. In my Eclipse run config, I added myproject/src/main/resources/webroot to the classpath, and I made sure my working directory was set to 'myproject'. When running from the command line, I'm in the myproject directory, and my script looks like this:
JAVA_OPTS="-Dmyproject.port=8099" CLASSPATH="src/main/java:src/main/resources:target/dependencies/*:target/classes" vertx run com.my.MyProject
No matter what, I always get 404s when I try any of these URLs:
http://localhost:8099
http://localhost:8099/
http://localhost:8099/index.html
http://localhost:8099/static/css/base.css
Anything else I need to be doing?
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. @FunctionalInterface public interface Handler<E> A generic event handler. This interface is used heavily throughout Vert.
Vert. x Web Client is an asynchronous HTTP and HTTP/2 client. The Web Client makes easy to do HTTP request/response interactions with a web server, and provides advanced features like: Json body encoding / decoding. request/response pumping.
public interface RoutingContext. Represents the context for the handling of a request in Vert. x-Web. A new instance is created for each HTTP request that is received in the Handler. handle(Object) of the router.
The solution I found depends on the answer to the questions: where is the static content going to be at runtime, and from where would you like vertx to serve it?
In my case, the installed file system would be where the static content was located (not in a jar file), and I wanted vertx to serve it from that location so that it could be updated live. So, I disabled classpath resolving in the StaticHandler by setting the JVM system property vertx.disableFileCPResolving
to true
at vertx startup.
Then I placed the webroot
folder under the directory from which the jvm is started. In my case, I'm using scripts that guarantee the jvm's cwd
is always <app-root>/bin
, so dropping the content in <app-root>/bin/webroot
was sufficient. If you can't make guarantees about where the jvm will be started from, it might be tougher, because you may need to pass an absolute path to StaticHandler.webroot() pointing to this fixed location, but I think there is an open issue regarding support for this (see here).
If the static content is going to be packaged into a jar, it's a little simpler. You can add 'webroot' as a resource in the jar and place all the content of interest in there. In this case you don't want to disable classpath resolving, so either set vertx.disableFileCPResolving
to false
, or don't set it at all. Now when you run vertx
, it will find webroot
in the jar file and extract its contents to <cwd>/.vertx/.file-cache-<guid>
(where cwd
is wherever you started the jvm from), and serve the contents from there. Note that this isn't viable if you intend to be able to do live updates to the content, because any changes to the files under that directory will get lost when vertx shuts down, because vertx-web
will delete that directory. On restart, it will retrieve the original files from the jar resource again, and changes will be lost.
Anyway, hope this helps.
Execute the following code at any breakpoint and it'll tell you the root directory from which it will resolve your resource request:
new File("").getCanonicalPath()
Request: localhost:8080/static/script.js
Handler:
router.route("/static/*").handler(StaticHandler.create("webroot"));
The file should be in MODULE_ROOT/webroot/script.js
Debug all the way into the StaticHandlerImpl#sendStatic
method to see how your files are being resolved, and use the debug expression window liberally to interrogate the file system.
The default classpath setting worked for me. I used the following hierarchy:
Keep the static file in main/resources/webroot
directory
<AppRoot>/src/main/resources/webroot/static/index.html
And initialize vertx as follows:
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler;
Vertx vertx = Vertx.vertx();
Router router = Router.router(vertx);
router.route().handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
Now you can access it from this URL: http://localhost:8080/static/
Refer vertx-web documentation for more info.
Alright, I'll somewhat answer my own question. First, I should point out that I was keying off of the static-content parts of the vertx-web docs (http://vertx.io/docs/vertx-web/js/#_serving_static_resources):
Any requests to paths handled by the static handler will result in files being served from a directory on the file system or from the classpath. The default static file directory is webroot but this can be configured.
In the following example all requests to paths starting with /static/ will get served from the directory webroot:
var StaticHandler = require("vertx-web-js/static_handler");
router.route("/static/*").handler(StaticHandler.create().handle);
and
Any requests to the root path / will cause the index page to be served. By default the index page is index.html. This can be configured with setIndexPage.
It seemed to me that if I didn't explicitly define a handler for "/", then index.html would implicitly be served. And it also seemed that just creating a StaticHandler and adding it to the router would suffice for CSS/JS/IMG resources. I think my assumptions were incorrect.
So I added the following, which seem to be what was needed:
first, I told the StaticHandler explicitly to look for a "webroot" folder:
router.route("/static/*").handler(StaticHandler.create("webroot").setCachingEnabled(false));
then, I explicitly added a route to my router to handle requests to "/":
router.route("/").handler(ctx -> {
Logger.log("Got an HTTP request to /");
ctx.response().sendFile("webroot/index.html").end();
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With