Documentation
Documentation here is always for the latest version of Spark. We don’t have the capacity to maintain separate docs for each version, but Spark is always backwards compatible.
Docs for (spark-kotlin) will arrive here ASAP. You can follow the progress of spark-kotlin on (GitHub)
Getting started
1: Create a new maven project and add the dependency to your POM.xml:
Java
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</artifactId> <version>2.9.4</version> </dependency>
Kotlin
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-kotlin</artifactId> <version>1.0.0-alpha</version> </dependency>
Not familiar with Maven? Click here for more detailed instructions.
Other dependency managers:
Gradle : compile "com.sparkjava:spark-core:2.9.4" // add to build.gradle (for Java users) Gradle : compile "com.sparkjava:spark-kotlin:1.0.0-alpha" // add to build.gradle (for Kotlin users) Ivy : <dependency org="com.sparkjava" name="spark-core" rev="2.9.4" conf="build" /> // ivy.xml SBT : libraryDependencies += "com.sparkjava" % "spark-core" % "2.9.4" // build.sbt
2: Start coding:
import static spark.Spark.*; public class HelloWorld { public static void main(String[] args) { get("/hello", (req, res) -> "Hello World"); } }
3: Run and view:
http://localhost:4567/hello
To see more console output from Spark (debug info, etc), you have to add a logger to your project.
Stopping the Server
By calling the stop() method the server is stopped and all routes are cleared.
stop();
Wait, what about starting the server?
The server is automatically started when you do something that requires the server to be started (i.e. declaring a route or setting the port).
You can also manually start the server by calling init()
.
You can specify what should happen if initialization fails:
initExceptionHandler((e) -> System.out.println("Uh-oh"));
The default behaviour is to log and shut down:
private Consumer<Exception> initExceptionHandler = (e) -> { LOG.error("ignite failed", e); System.exit(100); };
Routes
The main building block of a Spark application is a set of routes. A route is made up of three simple pieces:
- A verb (get, post, put, delete, head, trace, connect, options)
- A path (/hello, /users/:name)
- A callback (request, response) -> { }
Routes are matched in the order they are defined. The first route that matches the request is invoked.
Always statically import Spark methods to ensure good readability:
get("/", (request, response) -> { // Show something }); post("/", (request, response) -> { // Create something }); put("/", (request, response) -> { // Update something }); delete("/", (request, response) -> { // Annihilate something }); options("/", (request, response) -> { // Appease something });
Route patterns can include named parameters, accessible via the params()
method on the request object:
// matches "GET /hello/foo" and "GET /hello/bar" // request.params(":name") is 'foo' or 'bar' get("/hello/:name", (request, response) -> { return "Hello: " + request.params(":name"); });
Route patterns can also include splat (or wildcard) parameters. These parameters can be accessed by using the splat()
method on the request object:
// matches "GET /say/hello/to/world" // request.splat()[0] is 'hello' and request.splat()[1] 'world' get("/say/*/to/*", (request, response) -> { return "Number of splat parameters: " + request.splat().length; });
Route unmapping
Routes can be unmapped using the ‘unmap’ function:
unmap("/hello"); // unmaps all routes with path 'hello' unmap("/hello", "get"); // unmaps all 'GET' routes with path 'hello'
Path groups
If you have a lot of routes, it can be helpful to separate them into groups. This can be done by calling the path()
method, which takes a String prefix
and gives you a scope to declare routes and filters (or nested paths) in:
path("/api", () -> { before("/*", (q, a) -> log.info("Received api call")); path("/email", () -> { post("/add", EmailApi.addEmail); put("/change", EmailApi.changeEmail); delete("/remove", EmailApi.deleteEmail); }); path("/username", () -> { post("/add", UserApi.addUsername); put("/change", UserApi.changeUsername); delete("/remove", UserApi.deleteUsername); }); });
Request
Request information and functionality is provided by the request parameter:
request.attributes(); // the attributes list request.attribute("foo"); // value of foo attribute request.attribute("A", "V"); // sets value of attribute A to V request.body(); // request body sent by the client request.bodyAsBytes(); // request body as bytes request.contentLength(); // length of request body request.contentType(); // content type of request.body request.contextPath(); // the context path, e.g. "/hello" request.cookies(); // request cookies sent by the client request.headers(); // the HTTP header list request.headers("BAR"); // value of BAR header request.host(); // the host, e.g. "example.com" request.ip(); // client IP address request.params("foo"); // value of foo path parameter request.params(); // map with all parameters request.pathInfo(); // the path info request.port(); // the server port request.protocol(); // the protocol, e.g. HTTP/1.1 request.queryMap(); // the query map request.queryMap("foo"); // query map for a certain parameter request.queryParams(); // the query param list request.queryParams("FOO"); // value of FOO query param request.queryParamsValues("FOO") // all values of FOO query param request.raw(); // raw request handed in by Jetty request.requestMethod(); // The HTTP method (GET, ..etc) request.scheme(); // "http" request.servletPath(); // the servlet path, e.g. /result.jsp request.session(); // session management request.splat(); // splat (*) parameters request.uri(); // the uri, e.g. "http://example.com/foo" request.url(); // the url. e.g. "http://example.com/foo" request.userAgent(); // user agent
Response
Response information and functionality is provided by the response parameter:
response.body(); // get response content response.body("Hello"); // sets content to Hello response.header("FOO", "bar"); // sets header FOO with value bar response.raw(); // raw response handed in by Jetty response.redirect("/example"); // browser redirect to /example response.status(); // get the response status response.status(401); // set status code to 401 response.type(); // get the content type response.type("text/xml"); // set content type to text/xml
Query Maps
Query maps allows you to group parameters to a map by their prefix. This allows you to group two parameters like user[name]
and user[age]
to a user map.
request.queryMap().get("user", "name").value(); request.queryMap().get("user").get("name").value(); request.queryMap("user").get("age").integerValue(); request.queryMap("user").toMap();
Cookies
request.cookies(); // get map of all request cookies request.cookie("foo"); // access request cookie by name response.cookie("foo", "bar"); // set cookie with a value response.cookie("foo", "bar", 3600); // set cookie with a max-age response.cookie("foo", "bar", 3600, true); // secure cookie response.removeCookie("foo"); // remove cookie
Sessions
Every request has access to the session created on the server side, provided with the following methods:
request.session(true); // create and return session request.session().attribute("user"); // Get session attribute 'user' request.session().attribute("user","foo"); // Set session attribute 'user' request.session().removeAttribute("user"); // Remove session attribute 'user' request.session().attributes(); // Get all session attributes request.session().id(); // Get session id request.session().isNew(); // Check if session is new request.session().raw(); // Return servlet object
Halting
To immediately stop a request within a filter or route use halt()
:
halt(); // halt halt(401); // halt with status halt("Body Message"); // halt with message halt(401, "Go away!"); // halt with status and message
halt()
is not intended to be used inside exception-mappers.
Filters
Before-filters are evaluated before each request, and can read the request and read/modify the response.
To stop execution, use halt()
:
before((request, response) -> { boolean authenticated; // ... check if authenticated if (!authenticated) { halt(401, "You are not welcome here"); } });
After-filters are evaluated after each request, and can read the request and read/modify the response:
after((request, response) -> { response.header("foo", "set by after filter"); });
After-after-filters are evaluated after after-filters. Think of it as a “finally” block.
afterAfter((request, response) -> { response.header("foo", "set by afterAfter filter"); });
Filters optionally take a pattern, causing them to be evaluated only if the request path matches that pattern:
before("/protected/*", (request, response) -> { // ... check if authenticated halt(401, "Go Away!"); });
Redirects
You can trigger a browser redirect with the redirect method on the response:
response.redirect("/bar");
You can also trigger a browser redirect with specific HTTP 3XX status code:
response.redirect("/bar", 301); // moved permanently
Redirect API
There is also a convenience API for redirects which can be used directly without the response:
// redirect a GET to "/fromPath" to "/toPath" redirect.get("/fromPath", "/toPath"); // redirect a POST to "/fromPath" to "/toPath", with status 303 redirect.post("/fromPath", "/toPath", Redirect.Status.SEE_OTHER); // redirect any request to "/fromPath" to "/toPath" with status 301 redirect.any("/fromPath", "/toPath", Redirect.Status.MOVED_PERMANENTLY);
Remember to import Spark statically instead of prefixing it as Spark.redirect
Custom error handling
Not found (code 404) handling
// Using string/html notFound("<html><body><h1>Custom 404 handling</h1></body></html>");
// Using Route notFound((req, res) -> { res.type("application/json"); return "{\"message\":\"Custom 404\"}"; });
Internal server error (code 500) handling
// Using string/html internalServerError("<html><body><h1>Custom 500 handling</h1></body></html>");
// Using Route internalServerError((req, res) -> { res.type("application/json"); return "{\"message\":\"Custom 500 handling\"}"; });
Exception Mapping
To handle exceptions of a configured type for all routes and filters:
get("/throwexception", (request, response) -> { throw new YourCustomException(); }); exception(YourCustomException.class, (exception, request, response) -> { // Handle the exception here });
Static Files
You can assign a folder in the classpath serving static files with the staticFiles.location()
method. Note that the public directory name is not included in the URL.
A file /public/css/style.css
is made available as http://{host}:{port}/css/style.css
// root is 'src/main/resources', so put files in 'src/main/resources/public' staticFiles.location("/public"); // Static files
You can also assign an external folder (a folder not in the classpath) to serve static files by using the staticFiles.externalLocation()
method.\
staticFiles.externalLocation(System.getProperty("java.io.tmpdir"));
Static files location must be configured before route mapping. If your application has no routes, init()
must be called manually after location is set.
Cache/Expire time
You can specify the expire time (in seconds). By default there is no caching.
staticFiles.expireTime(600); // ten minutes
Setting custom headers
staticFiles.header("Key-1", "Value-1"); staticFiles.header("Key-1", "New-Value-1"); // Using the same key will overwrite value staticFiles.header("Key-2", "Value-2"); staticFiles.header("Key-3", "Value-3");
ResponseTransformer
Mapped routes that transform the output from the handle method. This is done by extending the ResponseTransformer
object and passing it to the mapping method. Example of a route transforming output to JSON using Gson:
import com.google.gson.Gson; public class JsonTransformer implements ResponseTransformer { private Gson gson = new Gson(); @Override public String render(Object model) { return gson.toJson(model); } }
and how it is used (MyMessage is a bean with one member ‘message’):
get("/hello", "application/json", (request, response) -> { return new MyMessage("Hello World"); }, new JsonTransformer());
You can also use Java 8 method references, since ResponseTransformer is an interface with one method:
Gson gson = new Gson(); get("/hello", (request, response) -> new MyMessage("Hello World"), gson::toJson);
Views and Templates
Spark has community-provided wrappers for a lot of popular template engines:
- Velocity (very mature, feature rich, great IDE support)
- Freemarker (very mature, feature rich, great IDE support)
- Mustache (mature, decent IDE support)
- Handlebars (mature, decent IDE support)
- Jade (mature, decent IDE support)
- Thymeleaf (mature, feature rich, decent IDE support)
- Pebble (we know very little about this)
- Water (we know very little about this)
- jTwig (we know very little about this)
- Jinjava (we know very little about this)
- Jetbrick (we know very little about this)
There are two main ways of rendering a template in Spark. You can either call render directly in a standard route declaration (recommended), or you can provide the template-engine as a third-route parameter (likely to be removed in the future):
// do this get("template-example", (req, res) -> { Map<String, Object> model = new HashMap<>(); return new VelocityTemplateEngine().render( new ModelAndView(model, "path-to-template") ); });
// don't do this get("template-example", (req, res) -> { Map<String, Object> model = new HashMap<>(); return new ModelAndView(model, "path-to-template"); }, new VelocityTemplateEngine());
It can be helpful to create a static utility method for rendering:
get("template-example", (req, res) -> { Map<String, Object> model = new HashMap<>(); return render(model, "path-to-template"); }); // declare this in a util-class public static String render(Map<String, Object> model, String templatePath) { return new VelocityTemplateEngine().render(new ModelAndView(model, templatePath)); }
Velocity
Renders HTML using the Velocity template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-velocity</artifactId> <version>2.7.1</version> </dependency>
Freemarker
Renders HTML using the Freemarker template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-freemarker</artifactId> <version>2.7.1</version> </dependency>
Mustache
Renders HTML using the Mustache template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-mustache</artifactId> <version>2.7.1</version> </dependency>
Handlebars
Renders HTML using the Handlebars template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-handlebars</artifactId> <version>2.7.1</version> </dependency>
Jade
Renders HTML using the Jade template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-jade</artifactId> <version>2.7.1</version> </dependency>
Thymeleaf
Renders HTML using the Thymeleaf template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-thymeleaf</artifactId> <version>2.7.1</version> </dependency>
Pebble
Renders HTML using the Pebble template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-pebble</artifactId> <version>2.7.1</version> </dependency>
Water
Renders HTML using the Water template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-water</artifactId> <version>2.7.1</version> </dependency>
Jtwig
Renders HTML using the Jtwig template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-jtwig</artifactId> <version>2.7.1</version> </dependency>
Jinjava
Renders HTML using the Jinjava template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-jinjava</artifactId> <version>2.7.1</version> </dependency>
Jetbrick
Renders HTML using the Jetbrick template engine. Source and example on GitHub.
<dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-jetbrick</artifactId> <version>2.7.1</version> </dependency>
Embedded web server
Standalone Spark runs on an embedded Jetty web server.
Port
By default, Spark runs on port 4567. If you want to set another port, use port()
. This has to be done before declaring routes and filters:
port(8080); // Spark will run on port 8080
Secure (HTTPS/SSL)
You can set the connection to be secure via the secure()
method.
This has to be done before any route mapping:
secure(keystoreFilePath, keystorePassword, truststoreFilePath, truststorePassword);
If you need more help, check out the FAQ.
ThreadPool
You can set the maximum number of threads easily:
int maxThreads = 8; threadPool(maxThreads);
You can also configure the minimum numbers of threads, and the idle timeout:
int maxThreads = 8; int minThreads = 2; int timeOutMillis = 30000; threadPool(maxThreads, minThreads, timeOutMillis);
Waiting for Initialization
You can use the method awaitInitialization()
to check if the server is ready to handle requests. This is usually done in a separate thread, for example to run a health check module after your server has started.
The method causes the current thread to wait until the embedded Jetty server has been initialized. Initialization is triggered by defining routes and/or filters. So, if you’re using just one thread don’t put this before you define your routes and/or filters.
awaitInitialization(); // Wait for server to be initialized
WebSockets
WebSockets provide a protocol full-duplex communication channel over a single TCP connection, meaning you can send message back and forth over the same connection.
WebSockets only works with the embedded Jetty server, and must be defined before regular HTTP routes. To create a WebSocket route, you need to provide a path and a handler class:
webSocket("/echo", EchoWebSocket.class); init(); // Needed if you don't define any HTTP routes after your WebSocket routes
import org.eclipse.jetty.websocket.api.*; import org.eclipse.jetty.websocket.api.annotations.*; import java.io.*; import java.util.*; import java.util.concurrent.*; @WebSocket public class EchoWebSocket { // Store sessions if you want to, for example, broadcast a message to all users private static final Queue<Session> sessions = new ConcurrentLinkedQueue<>(); @OnWebSocketConnect public void connected(Session session) { sessions.add(session); } @OnWebSocketClose public void closed(Session session, int statusCode, String reason) { sessions.remove(session); } @OnWebSocketMessage public void message(Session session, String message) throws IOException { System.out.println("Got: " + message); // Print message session.getRemote().sendString(message); // and send it back } }
Other web server
To run Spark on another web server (instead of the embedded jetty server), an implementation of the interface spark.servlet.SparkApplication
is needed. You have to initialize your routes in the init()
method, and the following filter might have to be configured in your web.xml:
<filter> <filter-name>SparkFilter</filter-name> <filter-class>spark.servlet.SparkFilter</filter-class> <init-param> <param-name>applicationClass</param-name> <param-value>com.company.YourApplication</param-value> </init-param> </filter> <filter-mapping> <filter-name>SparkFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
GZIP
GZIP is done automatically if it’s in both the request and the response headers. This usually only means that you have to set it in your response headers.
If you want to GZIP a single response, you can add it manually to your route:
get("/some-path", (request, response) -> { // code for your get response.header("Content-Encoding", "gzip"); });
If you want to GZIP everything, you can use an after-filter
after((request, response) -> { response.header("Content-Encoding", "gzip"); });
Javadoc
javadoc.io
Javadoc is available at javadoc.io.
Build it yourself
After getting the source from GitHub run:
mvn javadoc:javadoc
The result is put in /target/site/apidocs
Examples and FAQ
Examples can be found on the project’s page on GitHub.
How do I upload something?
Note: This applies to the standard configuration of Spark (embedded jetty). If you’re using Spark with some other webserver, this might not apply to you.
To upload a file you need a form and a post handler. First, create a form with the correct enctype, and an input field with the type “file” and a name of your choice (here “upoaded_file”):
<form method='post' enctype='multipart/form-data'> <input type='file' name='uploaded_file'> <button>Upload picture</button>" </form>"
For Spark to be able to extract the uploaded file, you have to set a specific request attribute, which allows to use the getPart()
method on the raw request:
post("/yourUploadPath", (request, response) -> { request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp")); try (InputStream is = request.raw().getPart("uploaded_file").getInputStream()) { // Use the input stream to create a file } return "File uploaded"; });
The Java-IO-stuff is left out as it’s not Spark-specific, but you can see a fully working example here.
How do I enable SSL/HTTPS?
Enabling HTTPS/SSL requires you to have a keystore file, which you can generate using the Java keytool (→ oracle docs). Once you have the keystore file, just point to its location and include its password.
String keyStoreLocation = "deploy/keystore.jks"; String keyStorePassword = "password"; secure(keyStoreLocation, keyStorePassword, null, null);
Check out the fully working example on GitHub if you need more guidance.
How do I enable logging?
You might have seen this message when starting Spark:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
To enable logging, just add the following dependency to your project:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.21</version> </dependency>
How do I enable automatic refresh of static files?
If you use staticFiles.location()
, meaning you keep your static files in the classpath, static resources are copied to a target folder when you build your application. This means you have to make/build your project in order to refresh static files. A workaround for this is to tell Spark to read static files from the absolute path to the src-directory. If you do this you will see changes instantly when you refresh, but if you build a jar file it will only work on your computer (because of the absolute path). So, only use this during development.
if (localhost) { String projectDir = System.getProperty("user.dir"); String staticDir = "/src/main/resources/public"; staticFiles.externalLocation(projectDir + staticDir); } else { staticFiles.location("/public"); }