Resources
Although most of Pedestal is focused on dynamic routes, nearly all HTTP-based applications will have some amount of static resources that need to be client accessible as well.
These resources take the form of either plain files on the file system, or virtual files available on the Java classpath.
The classpath includes not just the code of your application, but the code for the Pedestal library, and any other dependencies you have defined for your project.
For both classpath and file system resources, you must be careful about what you expose - only specific directories, known to be free of any compromising information (such as API keys or other secrets).
Resource Routes
The preferred approach is to add routes for resources to your application.
(ns org.example.service
(:require [io.pedestal.http :as http]
[io.pedestal.http.resources :as resources]
[io.pedestal.http.route :as route]))
(defn create-server
[]
(http/create-server
{::http/port 8890
::http/type :jetty
::http/join? false
::http/routes (route/routes-from
#{ ... } (1)
(resources/file-routes {:file-root "web/public"}))})) (2)
1 | This is where you would put the dynamic routes for your application. |
2 | This exposes any files stored in the web/public folder under the default URI path /public . |
The routes-from
macro will expand and combine the routes together to form the full routing table.
The resource-routes
function is analogous to file-routes
, but returns routes that expose resources on the classpath.
Each of these functions take a single parameter, a map of options:
Option | Default | Only | Description |
---|---|---|---|
:prefix |
"/public" |
The path prefix for the exposed resources. |
|
:root-path |
- |
file |
Path to root directory to expose. |
:resource-root |
- |
resource |
Prefix on resources, limiting access to a safe subset of the classpath |
:classloader |
- |
resource |
Classloader used to find resources |
:index-files? |
true |
file |
If true, paths that map to directories may return the index file for that directory. |
:cache? |
true |
If true, then resource data is cached to in memory to speed up requests. |
|
:fast? |
true |
If true, then fast, asynchronous responses may be used. |
|
:allow-head? |
true |
If true, then additional routes are added to support the :head request method, returning empty bodies. |
|
:route-namespace |
"io.pedestal.http.resources" |
Namespace used when creating route names for the routes. |
Using the defaults, which enable fast responses and caching, the route-based resources are demonstrably faster than the interceptor-based resources, especially for large resources … though for any reasonably sized resource, the response time is sub-millisecond.
Options notes
It is quite possible to use both file
and resource
together, as long as the :prefix is set so that routes do not overlap.
If necessary, it’s even possible to use these functions multiple times, but care must be taken with the :route-namespace to avoid
route name collisions.
The :prefix option should start with a "/" and not end with one.
For the :index-files? option, Pedestal will identify requests that map to a directory, not to a specific file. Pedestal will search for one of the following:
-
index.html
-
index.htm
-
Any file whose name starts with
index.
If any of these are found, then the request is treated as if it were to the matching file.
The :resource-root option should not start or end with a slash; to restrict access to just classpath files within the org.example.myapp.public
namespace, the option should be "org/example/myapp/public".
Requests that include any use of ".." in the path (in order to "escape" from the root location) are rejected, resulting in a 404 response.
The :cache? option does not currently put an upper limit on how many resources get cached; it’s presumed that there are only a finite number of resources that can be exposed and cached.
You will want to disable the :cache? option if you have more than a handful of resources that may be available, as there’s no facility to access or clear the cache. If you have many hundreds of resources, or the resources may change over time, caching is not a good option. You may want to implement your own cache as an interceptor around the resource routes. |
The :fast? option will attempt to convert matched files or resources into a java.nio.channel.Channel instance, for an asynchronous response. This only occurs if the size of the resource exceeds the buffer size of the servlet response.
The default used when the :classloader option is nil is the context class loader associated with the current thread.
Table route options
Finally, the options map will also be passed to
table-routes
, so you may use the keys it
supports, particularly :scheme, :port, and :interceptors.
Service map options
File and resource can also be exposed by configuration in the service map:
(defn create-server [] (http/create-server {::http/port 8890 ::http/type :jetty ::http/join? false ::http/file-path "web/public" ::http/routes (route/routes-from #{ ... })}))
This results in an interceptor being added to the default interceptor list; it is ordered immediately before the routing interceptor (created from the ::http/routes).
Because these are interceptors, and not routes, they are not visible in the routing table. They can overlap with actual routes without an error: any requests that are not processed as resources will drop down into the routing interceptor.
A second option, ::http/resource-path, is used to enable access to resources on the classpath.
io.pedestal.http.ring-middlewares
The service map options are implemented in terms of two functions:
A third option, fast-resource
, adds
support for fast, asynchronous replies for large resources on the classpath.
Each of these functions have additional options that can be configured when they are added explicitly as interceptors (rather than relying on the ::http/file-path or ::http/resource-path service map options).