Mittwoch, 20. März 2024 - Lesezeit: 2 Minuten
Benötigt man aktuell einen REST-Service, so klein und minimalistisch er auch sein wird, greift man heutzutage fast schon automatisch auf Frameworks wie Spring oder Spring Boot zurück. Solche Frameworks bringen dann viele zusätzliche Funktionen mit, die man im aktuellen Kontext vielleicht gar nicht braucht. Daher hier ein minimalistischer REST Service ohne jegliches Framework in knapp 60 Zeilen.
package de.itwerkstatt;
import com.sun.net.httpserver.*;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Base64;
import java.util.Objects;
public class Main {
private final static int serverPort = 8000;
private final static String validUser = "admin";
private final static String validPassword = "admin";
public static void main(String[] args) throws IOException {
// Erstellen eines HttpServers mit dem Port
HttpServer server = HttpServer.create(new InetSocketAddress(serverPort), 0);
// Erstellen eines Endpoints (Context) und dem Handler
HttpContext context = server.createContext("/api/hello", httpExchange -> {
// HttpExchange implementiert AutoClosable und schließt alle
// Streams am Ende automatisch
try (httpExchange) {
String respText = "Hallo " + httpExchange.getAttribute("greetingKey") + "!";
httpExchange.sendResponseHeaders(200, respText.getBytes().length);
OutputStream output = httpExchange.getResponseBody();
output.write(respText.getBytes());
output.flush();
}
});
//Einstellungen Endpoint (Context)
//Filter die vor dem Handler ausgeführt werden
context.getFilters().add(Filter.beforeHandler("Filter 1 vor Handler", (httpExchange -> System.out.println("Filter 1 v"))));
//Filter die nach dem Handler ausgeführt werden
context.getFilters().add(Filter.afterHandler("Filter 1 nach Handler", (httpExchange -> System.out.println("Filter 1 n"))));
//Konfiguration, die im Handler oder auch den Filtern verwendet werden können
context.getAttributes().put("greetingKey", "Welt");
//Absichern des Endpoints
context.setAuthenticator(
// Weitere Informationen hier: https://datatracker.ietf.org/doc/html/rfc1945#section-11.1
// Realm: Kurze Beschreibung, welcher Bereich hier geschützt wird
new BasicAuthenticator("Greeting the world") {
@Override
public boolean checkCredentials(String username, String password) {
System.out.println("Checking user '" + username + "' and password '" + password + "'");
return Objects.equals(username, validUser) && Objects.equals(password, validPassword);
}
});
// Wir verwenden den Standard Executor
server.setExecutor(null);
// Server starten
server.start();
System.out.println("Server gestartet.");
String credentialStringValid = Base64.getEncoder().encodeToString((validUser + ":" + validPassword).getBytes());
String credentialStringInvalid = Base64.getEncoder().encodeToString((validUser + "123:" + validPassword).getBytes());
String exampleCallValid = String.format("curl localhost:%d/api/hello -H 'Authorization: Basic %s' -v", serverPort, credentialStringValid);
String exampleCallInvalid = String.format("curl localhost:%d/api/hello -H 'Authorization: Basic %s' -v", serverPort, credentialStringInvalid);
System.out.println("Beispielaufruf: "+exampleCallValid);
System.out.println("Beispielaufruf 401: "+exampleCallInvalid);
}
}