1 of 22

Implementing a Web API with Spark

CS – Advanced Programming Concepts

2 of 22

Spark Overview

3 of 22

Spark Java

  • An open-source framework for building Java web applications and web APIs
  • Create routes and define methods for handling HTTP requests and returning HTTP responses
  • Use Java Lambdas to process requests
  • https://sparkjava.com/
  • Don’t confuse with Apache Spark (an engine for performing data analytics)

4 of 22

A Simple Spark Server

import spark.Spark;��public class SimpleHelloBYUServer {� public static void main(String[] args) {� Spark.get("/hello", (req, res) -> "Hello BYU!");� }�}

-------------

  • Listens on port 4567
  • Access with: http://localhost:4567/hello

5 of 22

Alternative Handler Implementation: Method Reference

import spark.Spark;

import spark.Request;

import spark.Response;��public class SimpleHelloBYUServer {

� public static void main(String[] args) {� Spark.get("/hello", SimpleHelloBYUServer::handleHello);� }

private static Object handleHello(Request req, Response res) {

return "Hello BYU!";

}�}

6 of 22

Alternative Handler Implementation: Route Class

import spark.Spark;

import spark.Request;

import spark.Response;

import spark.Route��public class SimpleHelloBYUServer {

� public static void main(String[] args) {� Spark.get("/hello", new HelloHandler());� }

}

class HelloHandler implements Route {

public Object handle(Request req, Response res) {

return "Hello BYU!";

}

}

7 of 22

Specifying the Port from the Command Line

import spark.Spark;��public class HelloBYUServer {� public static void main(String[] args) {� try {� int port = Integer.parseInt(args[0]);� Spark.port(port);�� createRoutes();�� Spark.awaitInitialization();� System.out.println("Listening on port " + port);� } catch(ArrayIndexOutOfBoundsException | NumberFormatException ex) {� System.err.println("Specify the port number as a command line parameter");� }� }�� private static void createRoutes() {� Spark.get("/hello", (req, res) -> "Hello BYU!");� }�}

8 of 22

Spark Routes

9 of 22

Spark Routes

get("/", (request, response) -> {

// Show something

});

post("/", (request, response) -> {

// Create something

});

put("/", (request, response) -> {

// Update something

});

delete("/", (request, response) -> {

// Delete something

});

Routes are matched in the order they are defined. The first route that matches the request is invoked.

10 of 22

Named Parameters

// matches "GET /hello/foo" and "GET /hello/bar" get("/hello/:name", (request, response) -> {

return "Hello: " + request.params(":name");

});

11 of 22

Wildcard Parameters

// 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;

});

12 of 22

Useful Request and Response Methods

Request

  • body() – retrieve the request body
  • headers() – retrieve all headers (as a set of strings-–Set<String>)
  • header(“…”) – retrieve the specified header
  • Refer to (https://sparkjava.com/documentation#request) for a complete list

Response

  • body(“…”) – set the response body (i.e. “Hello” sets the response body to “Hello”)
  • status(404) – sets the status code to 404 (not found)
  • Refer to (https://sparkjava.com/documentation#response) for a complete list

13 of 22

Serving Static Files

14 of 22

Serving Static Files / Web Applications

import spark.Spark;

�public class StaticFileServer {� public static void main(String[] args) {� try {� int port = Integer.parseInt(args[0]);� Spark.port(port);�� // Must be done before mapping routes� Spark.staticFiles.location("/public");�� createRoutes();�� Spark.awaitInitialization();� System.out.println("Listening on port " + port);� } catch(ArrayIndexOutOfBoundsException | NumberFormatException ex) {� System.err.println("Specify the port number as a command line parameter");� }� }�� private static void createRoutes() {� Spark.get("/hello", (req, res) -> "Hello BYU!");� }�}�

15 of 22

Serving Static Files (cont.)

  • Access with a url that does not include /public (i.e. http://localhost:8080/MyStaticFile.html
  • Spark expects the file(s) to be placed in a subdirectory of some directory that is available on the classpath (i.e /public goes in a directory on the classpath)
  • A file named index.html will be served from the base url (without needing to include /index.html)
  • The following structure is recommended in Intellij
    • A src/main/java directory marked as Sources Root
    • A src/main/resources directory marked as Resources Root
      • Resources Root directories are on the classpath so /public goes there

16 of 22

Overriding the Default Not Found Page

import spark.Spark;

�public class CustomNotFoundStaticFileServer {� public static void main(String[] args) {� try {� int port = Integer.parseInt(args[0]);� Spark.port(port);�� // Must be done before mapping routes� Spark.staticFiles.location("/public");

Spark.notFound("<html><body>My custom 404 page</body></html>");

17 of 22

Overriding the Default Not Found Page with Content From a File

Spark.notFound((req, res) -> {� res.type("text/html");� return readFromClasspathDirectory("/public/404.html");�});

private static String readFromClasspathDirectory(String file) {� try {� InputStream inputStream =

CustomNotFoundStaticFileServer2.class.getResourceAsStream(file);� BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));�� StringBuilder content = new StringBuilder();� String line;� while ((line = reader.readLine()) != null) {� content.append(line).append("\n");� }�� return content.toString();� } catch (IOException e) {� return file + " file not found.";� }�}

18 of 22

Filters

19 of 22

Filters

  • Filters provide a way to execute common code for multiple routes without code duplication:

before((request, response) -> {

boolean authenticated;

// ... check if authenticated

if (!authenticated) {

halt(401, "You are not welcome here");

}

});

  • Filters take an optional pattern to restrict the routes to which they are applied:

before("/protected/*", (request, response) -> {});

  • There are also after filters
  • You can have multiple before and/or after filters, which are executed in the order in which they appear

20 of 22

Before Filter Example

import spark.Spark;��public class BeforeFilterExample {� public static void main(String[] args) {� try {� int port = Integer.parseInt(args[0]);� Spark.port(port);�� createRoutes();�� Spark.awaitInitialization();� System.out.println("Listening on port " + port);� } catch(ArrayIndexOutOfBoundsException | NumberFormatException ex) {� System.err.println("Specify the port number as a command line parameter");� }� }�� private static void createRoutes() {� Spark.before((req, res) -> System.out.println(

"Executing route: " + req.pathInfo()));

� Spark.get("/hello", (req, res) -> "Hello BYU!");� }�}

21 of 22

Installation

22 of 22

Making Spark Java Available to Your Project

Three Ways:

  1. Add the dependency from File / Project Structure
    • Search for com.sparkjava and select the latest version
    • Be careful not to select Apache Spark
  2. Create a Maven project and add the dependency to your pom.xml file:

<dependency>

<groupId>com.sparkjava</groupId>

<artifactId>spark-core</artifactId>

<version>2.9.4</version>

</dependency>

  • Create a Gradle project and add the dependency to your build.gradle file

implementation group: 'com.sparkjava', name: 'spark-core', version: '2.9.4'