1 of 26

Implementing a Web API with Javalin

CS 240 – Advanced Software Construction

2 of 26

Javalin Overview

3 of 26

Javalin

  • An open-source framework for building Java web applications and web APIs
  • Create handler methods for handling HTTP requests and returning HTTP responses
  • Serve website files to web browsers
  • https://javalin.io/

4 of 26

A Simple Javalin Server

import io.javalin.Javalin;

public class SimpleHelloBYUServer {

public static void main(String[] args) {

Javalin.create()

.get("/hello",

ctx -> ctx.result("Hello BYU!"))

.start(8080);

}

}

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

  • Access with: http://localhost:8080/hello

5 of 26

Alternative Handler Implementation: Method Reference

public class SimpleHelloBYUServer {

public static void main(String[] args) {

Javalin.create()

.get("/hello", SimpleHelloBYUServer::handleHello)

.start(8080);

}

private static void handleHello(Context ctx) {

ctx.result("Hello BYU!");

}

}

6 of 26

Alternative Handler Implementation: Handler Class

public class SimpleHelloBYUServer {

public static void main(String[] args) {

Javalin.create()

.get("/hello", new HelloHandler())

.start(8080);

}

private static class HelloHandler implements Handler {

@Override

public void handle(@NotNull Context ctx)

throws Exception {

ctx.result("Hello BYU!");

}

}

}

7 of 26

Javalin Server with Handler Classes

public class HelloBYUServer {

public static void main(

String[] args) {

int requestedPort =

Integer.parseInt(args[0]);

var server =

new HelloBYUServer();

int port =

server.run(requestedPort);

System.out.println(

"Running on port " + port);

}

public int run(int requestedPort) {

Javalin javalinServer =

Javalin.create();

createHandlers(javalinServer);

javalinServer.start(

requestedPort);

return javalinServer.port();

}

private void createHandlers(

Javalin javalinServer) {

javalinServer.get("/hello",

new HelloBYUHandler());

// Other routes here

}

}

8 of 26

Simple Handler Class

import io.javalin.http.Context;

import io.javalin.http.Handler;

public class HelloBYUHandler implements Handler {

public void handle(Context context) {

context.result("Hello BYU!");

}

}

9 of 26

Simple Json Handler Class

import io.javalin.http.Context;

import io.javalin.http.Handler;

public class HelloBYUJsonHandler implements Handler {

public void handle(Context context) {

context.json("{message: Hello BYU!}");

}

}

10 of 26

Detailed Handler Class

import io.javalin.http.Context;

import io.javalin.http.Handler;

public class HelloBYUJsonHandler implements Handler {

public void handle(Context context) {

context.status(200);

context.contentType("application/json");

context.header("CS240", "Awesome!");

context.result("{message: Hello BYU!}");

}

}

11 of 26

Handlers

AKA: Routes

12 of 26

Endpoint Handlers

get("/", (ctx) -> {

// Show something

});

post("/", (ctx) -> {

// Create something

});

put("/", (ctx) -> {

// Update something

});

delete("/", (ctx) -> {

// Delete something

});

Handlers are matched in the order they are defined. The first handler that matches the request is invoked.

13 of 26

Before and After Handlers

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

before(context -> {

boolean authenticated;

// ... check if authenticated

if (!authenticated) {

throw new UnauthorizedResponse();

}

});

  • Throwing a response exception prevents further processing by other handlers
  • Responses can be thrown from before, after or endpoint handlers (see: https://javalin.io/documentation#default-responses)

14 of 26

Before and After Handlers (continued)

  • Before and after routes take an optional pattern to restrict the routes to which they are applied:

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

  • beforeMatched(...) only applies to routes that have a matching endpoint handler
  • Can have multiple before and/or after filters, which are executed in the order in which they appear

15 of 26

Named Parameters

// matches "GET /hello/foo" and "GET /hello/bar" get("/hello/{name}", context -> {

return "Hello: " + context.pathParam("name");

});

// matches "GET /hello/foo/bar"

get("/hello/<name>", context -> {

return "Hello: " + context.pathParam("name");

});

<...> allows slashes, {...} does not allow slashes

16 of 26

Wildcard Parameters

// matches "GET /hello/foo/bar"

get("/hello/*", context -> {

// url part matching * not available

// (use<...> syntax to access

});

17 of 26

Useful Request and Response Methods

Request

  • body() – retrieves the request body
  • headerMap() – retrieves all headers (as a Map<String, String>)
  • header(“…”) – retrieves the specified header
  • path() - retrieves request URL path

Response

  • result(“Hello”) – sets the response body to “Hello”
  • json(“{message: hello}”) - sets the response body and content type
  • header(“...”, “...”) - sets a header
  • contentType(“...”) sets the content type
  • status(404) – sets the status code to 404 (not found)

18 of 26

Error Handling

19 of 26

Error Handling

  • Handling exceptions:

app.exception(Exception.class, (e, ctx) -> {

// handle general exceptions here

// will not trigger if more specific exception-mapper found

});

  • Handling status codes:

app.error(404, ctx -> {

ctx.result("Generic 404 message");

});

20 of 26

Throwing Responses

  • There are special exceptions (responses) you can throw that result in the corresponding status code being sent to the client
  • These may be caught by an error handler
  • Response Classes:
    • RedirectResponse (302)
    • BadRequestResponse (400)
    • UnauthorizedResponse (401)
    • ForbiddenResponse (403)
    • NotFoundResponse (404)
    • MethodNotAllowedResponse (405)
    • ConflictResponse (409)
    • GoneResposne (410)
    • InternalServerErrorResponse (500)
    • BadGatewayResponse (502)
    • ServiceUnavailableResponse (503)
    • GatewayTimeoutResponse (504)

21 of 26

Error Handling Example

private void run() {

Javalin.create()

.get("/error", this::throwException)

.exception(Exception.class, this::exceptionHandler)

.error(404, this::notFound)

.start(8080);

}

private void throwException(Context context) {

throw new RuntimeException("The server is on fire!");

}

private void exceptionHandler(Exception e, Context context) {

var body = new Gson().toJson(Map.of("message",

String.format("Error: %s", e.getMessage()), "success", false));

context.status(500);

context.json(body);

}

private void notFound(Context context) {

String msg = String.format("[%s] %s not found", context.method(), context.path());

var body = new Gson().toJson(Map.of("message", String.format("Error: %s", msg), "success", false));

context.json(body);

}

22 of 26

Serving Static Files

23 of 26

Serving Static Files / Web Applications

import io.javalin.Javalin;

public class StaticFileServer {

public int run(int requestedPort) {

Javalin javalinServer = Javalin.create(

config -> config.staticFiles.add("web")

);

}

}

24 of 26

Serving Static Files (cont.)

  • Access with a url that does not include /web (i.e. http://localhost:8080/MyStaticFile.html
  • Javalin expects the file(s) to be placed in a subdirectory of some directory that is available on the classpath (i.e /web 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 /web goes there

25 of 26

Installation

26 of 26

Making Javalin Available to Your Project

Three Ways:

  • Add the dependency from File / Project Structure
    • Search for javalin and select the latest version (io.javalin:javalin)
  • Create a Maven project and add the dependency to your pom.xml file:

<dependency>

<groupId>io.javalin</groupId>

<artifactId>javalin</artifactId>

<version>6.4.0</version>

</dependency>

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

implementation group: 'io.javalin', name: 'javalin', version: '6.4.0'