Static files ============ Serve static files on disk -------------------------- Project directory layout: :: config public favicon.ico robots.txt 404.html 500.html img myimage.png css mystyle.css js myscript.js src build.sbt Xitrum automatically serves static files inside ``public`` directory. URLs to them are in the form: :: /img/myimage.png /css/mystyle.css /css/mystyle.min.css To refer to them: :: To serve normal file in development environment and its minimized version in production environment (mystyle.css and mystyle.min.css as above): :: To send a static file on disk from your action, use ``respondFile``. :: respondFile("/absolute/path") respondFile("path/relative/to/the/current/working/directory") To optimize static file serving speed, you can avoid unnecessary file existence check with regex filter. If request url does not match pathRegex, Xitrum will respond 404 for that request. See ``pathRegex`` in ``config/xitrum.conf``. index.html fallback ------------------- If there's no route (no action) for URL ``/foo/bar`` (or ``/foo/bar/``), Xitrum will try to look for static file ``public/foo/bar/index.html`` (in the "public" directory). If the file exists, Xitrum will respond it to the client. 404 and 500 ----------- 404.html and 500.html in ``public`` directory are used when there's no matching route and there's error processing request, respectively. If you want to use your own error handler: :: import xitrum.Action import xitrum.annotation.{Error404, Error500} @Error404 class My404ErrorHandlerAction extends Action { def execute() { if (isAjax) jsRespond("alert(" + jsEscape("Not Found") + ")") else renderInlineView("Not Found") } } @Error500 class My500ErrorHandlerAction extends Action { def execute() { if (isAjax) jsRespond("alert(" + jsEscape("Internal Server Error") + ")") else renderInlineView("Internal Server Error") } } Response status is set to 404 or 500 before the actions are executed, so you don't have to set yourself. Serve resource files in classpath with WebJars convention --------------------------------------------------------- WebJars ~~~~~~~ `WebJars `_ provides a lot of web libraries that you can declare as a dependency in your project. For example, if you want to use `Underscore.js `_, declare in your project's ``build.sbt`` like this: :: libraryDependencies += "org.webjars" % "underscorejs" % "1.6.0-3" Then in your .jade template file: :: script(src={webJarsUrl("underscorejs/1.6.0", "underscore.js", "underscore-min.js")}) Xitrum will automatically use ``underscore.js`` for development environment and ``underscore-min.js`` for production environment. The result will look like this: :: /webjars/underscorejs/1.6.0/underscore.js?XOKgP8_KIpqz9yUqZ1aVzw If you want to use the same file for both environments: :: script(src={webJarsUrl("underscorejs/1.6.0/underscore.js")}) Dependencies of a dependency are automatically downloaded. If you see that the version you want is not selected (you can confirm by running ``sbt xitrum-package`` and see files in the created directory ``target/xitrum/lib``), you can force it with ``dependencyOverrides``. For example, if you see that jQuery 2.x is selected, but you want to support Internet Explorer 6, 7, or 8, you need to force jQuery 1.x like this: :: dependencyOverrides += "org.webjars" % "jquery" % "1.11.3" Save resource file inside .jar file with WebJars convention ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are a library developer and want to serve myimage.png from your library, which is a .jar file in classpath, save myimage.png in your .jar file with `WebJars `_ convention, example: :: META-INF/resources/webjars/mylib/1.0/myimage.png To serve it: :: In both development and production environments, the URL will be: :: /webjars/mylib/1.0/myimage.png?xyz123 Respond a file in classpath ~~~~~~~~~~~~~~~~~~~~~~~~~~~ To respond a file inside an classpath element (a .jar file or a directory), even when the file is not saved with `WebJars `_ convention: :: respondResource("path/relative/to/the/classpath/element") Ex: :: respondResource("akka/actor/Actor.class") respondResource("META-INF/resources/webjars/underscorejs/1.6.0/underscore.js") respondResource("META-INF/resources/webjars/underscorejs/1.6.0/underscore-min.js") Client side cache with ETag and max-age --------------------------------------- Xitrum automatically adds `Etag `_ for static files on disk and in classpath. ETags for small files are MD5 of file content. They are cached for later use. Keys of cache entries are ``(file path, modified time)``. Because modified time on different servers may differ, each web server in a cluster has its own local ETag cache. For big files, only modified time is used as ETag. This is not perfect because not identical file on different servers may have different ETag, but it is still better than no ETag at all. ``publicUrl`` and ``webJarsUrl`` automatically add ETag to the URLs they generate. For example: :: webJarsUrl("jquery/2.1.1/jquery.min.js") => /webjars/jquery/2.1.1/jquery.min.js?0CHJg71ucpG0OlzB-y6-mQ Xitrum also sets ``max-age`` and ``Expires`` headers to `one year `_. Don't worry that browsers do not pickup a latest file when you change it. Because when a file on disk changes, its ``modified time`` changes, thus the URLs generated by ``publicUrl`` and ``webJarsUrl`` also change. Its ETag cache is also updated because the cache key changes. GZIP ---- Xitrum automatically gzips textual responses. It checks the ``Content-Type`` header to determine if a response is textual: ``text/html``, ``xml/application`` etc. Xitrum always gzips static textual files, but for dynamic textual responses, for overall performance reason it does not gzips response smaller than 1 KB. Server side cache ----------------- To avoid loading files from disk, Xitrum caches small static files (not only textual) in memory with LRU (Least Recently Used) expiration. See ``small_static_file_size_in_kb`` and ``max_cached_small_static_files`` in ``config/xitrum.conf``.