Skip to main content
Dat 2. semester
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Javalin how-to

Javalin

Javalin er et lille Java-framework, som gør det muligt at lave en webserver i Java.

I vores projekter bruger vi Javalin til at modtage HTTP-requests fra browseren og sende responses tilbage.

Et meget enkelt eksempel:

var app = Javalin.create(config -> {
    config.routes.get("/", ctx -> ctx.result("Hej verden"));
}).start(7070);

Når serveren kører, kan vi åbne:

http://localhost:7070/

og få:

Hej verden

1. Routes

En route fortæller Javalin, hvad der skal ske, når en bestemt HTTP-request kommer ind.

En route består grundlæggende af:

  • en HTTP-metode
  • en path
  • en handler

GET

config.routes.get("/", ctx -> {
    ctx.result("Hej verden");
});

eller kort:

config.routes.get("/", ctx -> ctx.result("Hej verden"));

En route til /users:

config.routes.get("/users", ctx -> {
    ctx.result("Her er brugerne");
});

Hvis browseren laver:

GET /users

bliver handleren udført.

POST

POST bruges typisk, når browseren sender data til serveren, for eksempel fra en formular.

config.routes.post("/login", ctx -> {
    ctx.result("Du forsøger at logge ind");
});

GET og POST er forskellige HTTP-metoder, selv om de godt kan have samme path:

config.routes.get("/login", ctx -> {
    ctx.render("login.html");
});

config.routes.post("/login", ctx -> {
    // behandl login
});

Det er meget almindeligt at bruge GET til at vise en formular og POST til at behandle den.

2. Context-objektet

ctx er Javalins context for den aktuelle request.

Det er gennem ctx, at vi blandt andet kan:

  • læse data fra requesten
  • hente parametre
  • sende en response
  • sætte statuskode
  • gemme data til en template
  • arbejde med sessionen

Et meget enkelt eksempel:

config.routes.get("/hello", ctx -> {
    ctx.result("Hello!");
});

Her er ctx det objekt, vi bruger til at sende "Hello!" tilbage til browseren.

Response body

ctx.result("Hej verden");

Sender tekst tilbage til klienten.

HTML kan også sendes som response:

ctx.html("<h1>Hej verden</h1>");

I vores projekter vil vi normalt bruge Thymeleaf til HTML-sider i stedet.

Statuskode

ctx.status(404);

Sætter HTTP-statuskoden til 404.

Det kan kombineres med en besked:

ctx.status(404).result("Brugeren blev ikke fundet");

3. Organisering af routes

Når applikationen vokser, bliver det hurtigt upraktisk at have al kode i Main.

Vi placerer derfor routes og request-håndtering i controllers.

En simpel struktur kan være:

src/main/java
├── controllers
│   └── UserController.java
├── entities
│   └── User.java
├── factories
│   └── UserFactory.java
└── services
    └── UserService.java

Controlleren tager sig af kommunikationen med webdelen.

Eksempel:

public class UserController {

    public static void login(Context ctx, UserService userService) {
        // hent data fra request
        // kald service
        // send response
    }
}

Routes kan derefter pege på controllerens metoder:

config.routes.post("/login",
        ctx -> UserController.login(ctx, userService));

Controlleren skal ikke indeholde al forretningslogikken.

Et typisk flow er:

Browser
Controller
Service
Mapper / database

Controlleren arbejder med HTTP.

Servicen arbejder med programmets logik.

Mapperen arbejder med databasen.

4. Form-parametre

Når en HTML-formular sender data til Javalin, kan vi hente værdierne med ctx.formParam().

HTML:

<form action="/login" method="post">
    <input type="text" name="username">
    <input type="password" name="password">
    <button type="submit">Log ind</button>
</form>

Controller:

String username = ctx.formParam("username");
String password = ctx.formParam("password");

Det vigtige er forbindelsen mellem HTML og Java:

name="username"

bliver til:

ctx.formParam("username")

Hvis navnene ikke passer sammen, får du ikke den forventede værdi.

Det er derfor en klassisk fejl at skrive:

<input name="userName">

og forsøge at hente:

ctx.formParam("username");

De to navne er forskellige.

Kontrollér input

Form-parametre kommer ind som tekst.

String ageText = ctx.formParam("age");

Hvis vi skal bruge et tal, skal vi selv konvertere:

int age = Integer.parseInt(ageText);

Input fra brugeren kan være ugyldigt, så konvertering bør håndteres, når det er relevant.

5. Query-parametre

Query-parametre sendes som en del af URL’en.

Eksempel:

/users?group=1&page=2

Her er:

group = 1
page = 2

De kan hentes med:

String group = ctx.queryParam("group");
String page = ctx.queryParam("page");

Hvis værdien skal bruges som et tal:

int page = Integer.parseInt(ctx.queryParam("page"));

Query-parametre er især nyttige, når brugeren foretager en GET-request.

HTML:

<a href="/users?group=1&page=2">
    Vis brugere
</a>

Controller:

config.routes.get("/users", ctx -> {

    String group = ctx.queryParam("group");
    String page = ctx.queryParam("page");

});

6. Path-parametre

En værdi kan også være en del af selve URL’en.

/users/42

Route:

config.routes.get("/users/{id}", ctx -> {

    String id = ctx.pathParam("id");

});

Her er 42 værdien af id.

Vi kan for eksempel konvertere den:

int id = Integer.parseInt(ctx.pathParam("id"));

Path-parametre er praktiske, når URL’en identificerer en bestemt ressource.

/users/42
/tasks/17
/products/5

7. Redirect

En controller kan sende browseren videre til en anden route.

ctx.redirect("/users");

Det er ofte relevant efter en POST.

For eksempel:

config.routes.post("/login", ctx -> {

    // log brugeren ind

    ctx.redirect("/tasks");
});

Browseren bliver derefter sendt til /tasks.

Et typisk flow kan derfor være:

POST /login
Login behandles
redirect("/tasks")
GET /tasks
Vis tasks

8. Render en Thymeleaf-side

Når vi bruger Thymeleaf, kan Javalin rendere en template:

ctx.render("login.html");

Templates ligger i:

src/main/resources/templates

En controller kan også sende data med:

ctx.attribute("user", user);
ctx.render("profile.html");

Thymeleaf kan derefter bruge user i HTML’en.

<h1 th:text="${user.username}"></h1>

Se Thymeleaf-opslagsværket for flere eksempler.

9. Static files

CSS, billeder og almindelige HTML-filer kan ligge i public.

src/main/resources
├── public
│   ├── css
│   └── images
└── templates

Javalin kan konfigureres til at servere filer fra public:

Javalin.create(config -> {
    config.staticFiles.add("/public");
});

Javalin matcher først dine endpoints og forsøger derefter at finde en static file. Hvis ingen route eller static file matcher, får klienten en 404.

10. En komplet lille route

Her er et eksempel, hvor flere ting sættes sammen:

config.routes.post("/login", ctx -> {

    String username = ctx.formParam("username");
    String password = ctx.formParam("password");

    User user = userService.login(username, password);

    if (user != null) {
        ctx.sessionAttribute("user", user);
        ctx.redirect("/tasks");
    } else {
        ctx.status(401);
        ctx.render("login.html");
    }
});

Controlleren:

public static void login(Context ctx, UserService userService) {

    String username = ctx.formParam("username");
    String password = ctx.formParam("password");

    User user = userService.login(username, password);

    if (user != null) {
        ctx.sessionAttribute("user", user);
        ctx.redirect("/tasks");
    } else {
        ctx.status(401);
        ctx.render("login.html");
    }
}

Og routen:

config.routes.post("/login",
        ctx -> UserController.login(ctx, userService));

Det er denne type opdeling, vi bruger, når applikationen vokser.

11. Hvornår skal jeg bruge hvad?

Jeg vil…Jeg bruger…
Vise en sideGET
Sende data fra en formularPOST
Hente en formværdictx.formParam()
Hente en værdi fra URL’en efter ?ctx.queryParam()
Hente en værdi fra en pathctx.pathParam()
Sende tekst tilbagectx.result()
Sende HTML tilbagectx.html()
Vise en Thymeleaf-templatectx.render()
Sende data til Thymeleafctx.attribute()
Gemme data i sessionenctx.sessionAttribute()
Sende browseren til en anden routectx.redirect()
Sætte HTTP-statusctx.status()

12. Husk

Javalin tager sig af webkommunikationen.

Controlleren tager sig af request og response.

Servicen tager sig af programmets logik.

Thymeleaf tager sig af præsentationen.

Databasen kommer senere:

Browser
Javalin
Controller
Service
Mapper
PostgreSQL

Du behøver ikke kunne huske alle metoderne. Det vigtigste er at vide, hvad du leder efter, når du skal løse en konkret opgave.