Inhaltsverzeichnis

Arthur Dent hatte schon lange keinen so schlechten Tag mehr wie den, an dem die Erde in die Luft flog. 🔥 Halt, Moment, Stop. Falsches Buch. 📘 Hier geht es darum, den Server in unsere Webentwicklung miteinzubeziehen. Wir schauen uns daher zunächst das HTTP-Protokoll an und lernen dann, wie Java auf dem Server damit umgeht.

Lernziele dieser Einheit

Nach Abschluss dieser Einheit kannst du …

🏁

Das HyperText Transfer Protocol

Bildnachweis: Pixabay: rawpixel

Was passiert beim Aufruf einer Webseite?

Unsere Geschichte beginnt mit einen Browser und einem Webserver.

Und einem Anwender, der eine URL eingibt.

Daraufhin baut der Browser eine Verbindung zum Server auf.

Und sendet ihm eine Anfrage, um die Seite abzurufen. In diesem Fall eine GET-Anfrage.

Der Server antwortet mit Statuscode 200 und dem Inhalt der Seite.

Sogleich macht sich der Browser daran die Seite darzustellen.

Doch halt! Die Seite enthält ja noch Stylesheets, Skripte und Bilder.

Diese muss der Browser alle vom Server abrufen.

Und der Server antwortet fleißig mit den gesuchten Dateien.

Am Ende ist die Seite vollständig aufgebaut und die Verbindung wird getrennt.

🖼️ Nächstes Bild anzeigen

Bildnachweise: Pixabay: OpenClipart-Vectors, Pixabay: Clker-Free-Vector-Images, Pixabay: ROverhate, Pixabay: Jsprzperez

Inahlt einer HTTP-Nachricht

Verb Pfad HTTP-Version
POST /feedback/save.php HTTP/1.1 Anfragezeile
Host: www.wpvs.de
Content-Type: text/json
Accept: text/json
Connection: keep-alive
Header Fields ¹
  Leerzeile
{
    "course": "webprog",
    "year": 2017,
    "text": "Vielen Dank für diese tolle Vorlesung!"
}
Request Body ¹
HTTP-Version Statuscode Bezeichnung
HTTP/1.1 200 Ok Antwortzeile
Content-Type: text/json
Content-Length: 55
Date: Sun, 15 Oct 2017 20:18:17 GMT
Header Fields
  Leerzeile
{
    "type": "success",
    "message": "Feedback wurde gespeichert",
}
Response Body

¹ Optional

GET

  • Inhalte vom Server abrufen
  • Die Anfrage darf keinen Request Body enthalten
  • Der Server antwortet mit dem gewünschten Inhalt oder einer Fehlerseite

PUT

  • Neue Inhalte auf den Server hochladen
  • Der Inhalt der Datei muss im Request Body mitgeschickt werden
  • Der Server antwortet mit einer neuen HTML-Seite oder einer Fehlerseite

POST

  • Daten zur Verarbeitung an den Server schicken
  • Wird häufig verwendet, um vorhandene Daten auf dem Server zu aktualisieren
  • Der Server antwortet mit einer neuen HTML-Seite oder einer Fehlerseite

DELETE

  • Daten auf dem Server löschen
  • Die Anfrage darf keinen Request Body enthalten
  • Der Server antwortet mit einer neuen HTML-Seite oder einer Fehlerseite

Seltener verwendete Verben

HEAD, OPTIONS, TRACE, PATCH

1xx: Informationen

  • Zwischenmeldung, um die Verbindung aufrecht zu erhalten
  • Verhindert, dass die Verbindung durch einen Timeout vorzeitig beendet wird

2xx: Erfolgreicher Abschluss

  • Die Anfrage wurde erfolgreich bearbeitet
  • 200 OK: Alles roger! Der Response Body beinhaltet die gewünschten Daten 👌🏼
  • 202 Accepted: Die Anfrage wurde empfangen, wird aber erst später bearbeitet

3xx: Umleitung

  • Es sind weitere Aktionen vom Client notwendig
  • 300 Multiple Choice: Die Anfrage war nicht eindeutig genug
  • 301 Moved Permanently: Umleitung des Clients auf eine andere URL

4xx: Client Fehler

  • Der Client hat etwas falsch gemacht; die Verarbeitung wird abgelehnt
  • 400 Bad Request: Ungültige bzw. fehlerhafte Anfrage
  • 401 Unauthorized: Keine Berechtigung für diese Aktion
  • 403 Forbidden: Die Aktion ist prinzipiell nicht erlaubt
  • 404 Not Found: Der gesuchte Inhalt wurde nicht gefunden

5xx: Server Fehler

  • Anfrage kann aufgrund eines Serverfehlers nicht bearbeitet werden
  • 500 Internal Server Error: Schwerer technischer Fehler, alles ist kaputt! 🗑️
  • 501 Not Implemented: Die Funktion ist nicht verfügbar
  • 503 Service Unavailable: Temporärer Fehler, bitte später nochmal versuchen

HTTP/0.9

Dies war die ursprüngliche von Tim Berners-Lee entwickelte HTTP-Version, wie sie 1991 veröffentlicht wurde. In dieser Version gab es nur GET-Anfragen und noch keine Header Fields. Eine Anfrage bestand daher lediglich aus einer Zeile:

GET /index.html

Die Antwort des Servers beinhalte nur den Inhalt der abgerufenen Datei, sonst nichts.

HTTP/1.0

1996 wurde HTTP/1.0 veröffentlicht. Ab hier haben die HTTP-Nachrichten den oben gezeigten Aufbau.

HTTP/1.1

Diese Version wurde 1999 veröffentlicht und brachte weitreichende Verbesserungen. Unter anderem die Möglichkeit, die Verbindung zwischen zwei Anfragen offen zu halten. Denn zuvor wurde die Verbindung nach jeder Antwort automatisch getrennt. Da aber gerade der Verbindungsaufbau bei TCP/IP sehr langsam ist, führte dies zu langen Wartezeiten, wenn mehrere Dateien vom selben Server abgerufen werden mussten.

HTTP/2

2015 wurde HTTP/2 offiziell verabschiedet. In seinen Grundzügen basiert es auf dem von Google entwickelten SPDY-Protokoll. Die Inhalte der HTTP-Nachrichten sind weitgehend gleich geblieben, jedoch werden sie nicht mehr in der oben gezeigten Textform kodiert. Stattdessen kommt ein kompaktes und in vielen Dingen eindeutiger spezifiziertes Binärformat zum Einsatz.¹

Als weitere größere Neuerung führt HTTP/2 Server Push ein, wodurch der Server beim Abruf einer HTML-Seite selbstständig weitere Dateien an den Browser schicken kann. Somit können Stylesheets, Skripte und Bilder schon an den Browser geschickt werden, bevor dieser überhaupt weiß, dass er sie benötigen wird.

Stand 2017 hat sich HTTP/2 noch nicht gegen HTTP/1 durchgesetzt. Die meisten Webseiten verwenden weiterhin HTTP/1. Man kann aber davon ausgehen, dass sich das im Laufe der Jahre ändern wird.

¹ Beispielsweise war bei HTTP/1.x nie definiert, welche Bytefolge ein Zeilenende markiert.

Zeit für eine Demonstration

Bildnachweis für das Endesymbol: Pixabay: janf93

Aufzeichnung der HTTP-Kommunikation im Browser

Bildnachweis für das Endesymbol: Pixabay: janf93

Aufgabe 1: Ein kleines HTTP-Quiz

Aufgabe 1.1: HTTP-Verben

a) Was bedeutet das HTTP-Verb POST?

  1. Vorhandene Daten vom Server abrufen
  2. Daten auf dem Server löschen (POST = Postpone)
  3. Daten zur Verarbeitung an den Server schicken

b) Was bedeutet das HTTP-Verb DELETE?

  1. Daten im Browser löschen
  2. Daten auf dem Server löschen
  3. Prüfen, ob die Daten gelöscht werden können

c) Was bedeutet das HTTP-Verb GET?

  1. Daten vom Server abrufen, zum Beispiel eine HTML-Datei
  2. Abrufen einer Dateiliste vom Server mit allen Dateien einer Webseite
  3. Übertrag einer Datei vom Browser an den Server

Aufgabe 1.2: Statuscodes

a) Was bedeutet der Statuscode 401 Unauthorized?

  1. Die gesuchten Daten wurden nicht gefunden
  2. Die gewünschte Funktion wurde nicht ausprogrammiert
  3. Der Anwender ist für die gewünschte Funktion nicht berechtigt

b) Was bedeutet der Statuscode 503 Service Unavailble?

  1. Es ist ein temporärer Fehler aufgetreten, bitte später nochmal versuchen
  2. Es ist ein schwerwiegender Fehler aufgetreten
  3. Die gesuchte Information wurde nicht gefunden

c) Welcher Statuscode signalisiert, dass die Seite nicht gefunden wurde?

  1. 200
  2. 301
  3. 404

d) Welcher Statuscode wird geschickt, wenn alles okay ist?

  1. 100
  2. 200
  3. 300

Lösung: Aufgabe 1.1: 3, 2, 1; Aufgabe 1.2: 3, 1, 3, 2

Serverseitige Webanwendungen mit Java

Bildnachweis: Pixabay: rawpixel

Die drei Arten von Webanwendungen

Bildnachweis für das Smartphone: Pixabay: OpenClipart-Vectors

  • Die komplette Anwendungslogik befindet sich im Browser (JavaScript)
  • Die Anwendung kann lokal komplett ohne Server ausgeführt werden
  • Ein Server wird nur benötigt, wenn die Anwendung über das Internet aufrufbar sein soll
  • Mit Phonegap/Cordova erstelle mobile Anwendungen funktionieren auch nach diesem Prinzip

So haben wir bisher gearbeitet.

  • Der Klassiker: Die Anwendungslogik liegt komplett auf dem Server
  • JavaScript kommt im Browser wenn überhaupt nur sehr sparsam zum Einsatz
  • Stattdessen führt jede Aktion dazu, dass eine neue Seite vom Server geladen wird
  • Der Server führt dabei die Anwendungslogik aus und generiert das anzuzeigende HTML

Das schauen wir uns heute etwas genauer an.

  • Das Beste beider Welten: Browser und Server beinhalten je einen Teil der Anwendungslogik
  • Der HTML-Code der Seite kann zur Laufzeit generiert werden oder in einer einfachen HTML-Datei liegen
  • Bei verschiedenen Aktionen werden mit JavaScript weitere Daten mit dem Server ausgetauscht
  • Die empfangenen Daten werden dabei durch geschickte DOM-Manipulation sichtbar gemacht

Das behandeln wir ein anderes mal.

Wer die Wahl hat, hat die Qual

Statische Dateien vs. Javacode

1. Der Webserver empfängt eine HTTP-Anfrage.
2. Da sich die Anfrage auf eine einfache Datei bezieht, sucht der Server die Datei im Dateisystem.
3. Kann der Server die Datei finden, schickt er als Antwort den Statuscode 200 sowie den Inhalt der Datei.
1. Der Webserver empfängt eine HTTP-Anfrage.
2. Dieses mal bezieht sich die Anfrage auf ein Servlet oder eine Java Server Page.
3. Der Server führt das Servlet bzw. die Java Server Page aus, um eine Antwort zu generieren.

Webcontainer vs. eingebetter Webserver

Die klassische Form der Webentwicklung in Java. Ein ein Java geschriebener Webserver (der sogenannte Webcontainer bzw. Applikationsserver) beherbergt eine beliebige Anzahl an Webanwendungen. Die Webanwendungen müssen hierfür als Webarchive verpackt und im Server deployed werden (Deployment = Die Webanwendung mit dem Server zusammenbringen).

Historisch bedingt sind Webcontainer große, schwergewichtige Applikationsserver, die viele Ressourcen benötigen und je nach Einsatzgebiet umfangreich konfiguriert werden wollen. Aber es ist wie mit jedem Schlachtschiff: 🚢 Einmal in Dienst gestellt verrichtet es viele Jahre lang seine Arbeit.

Bekannte Applikationsserver sind: Tomcat, Jetty, GlassFish, Wildfly.

Moderne Alternative zu herkömmlichen Webcontainern: Bring Your Own Webserver. Bei der Webanwendung handelt es sich um ein gewöhnliches Javaprogramm mit einem integrierten Webserver. Dieser Ansatz kommt vor allem bei Microservices und 12-Faktor-Apps zum Einsatz.

Natürlich musst du nicht gleich einen eigenen Webserver programmieren. Hierfür gibt es fertige Bibliotheken, die du in deine Anwendung einbauen kannst. Im Vergleich zu einer herkömmlichen Webanwendung ist sie dadurch viel einfacher in Betrieb zu nehmen und verbraucht in der Regel auch weniger Ressourcen. 🛥️

Bekannte HTTP-Server-Bibliotheken sind: Die Klasse HttpServer, NanoHTTPD, Undertow, Spark, Jetty.

Unsere erste Java Server Page

<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>JSP-Beispiel</title> </head> <body> <form method="post"> Name: <input name="dein_name"/> Alter: <input name="dein_alter"/> <input type="hidden" name="aktion" value="zeigen"/> <input type="submit" value="Los!"/> </form> <!-- Alles zwischen <% und %> ist Javacode --> <% if (request.getMethod().equals("POST")) { %> <h2>Bisherige Eingabe</h2> <b>Name: </b> <%= request.getParameter("dein_name") %> <br/> <b>Alter:</b> <%= request.getParameter("dein_alter") %> <br/> <% } %> </body> </html>

Der HTML-Code aus Sicht des Browsers:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>JSP-Beispiel</title> </head> <body> <form method="post"> Name: <input name="dein_name"/> Alter: <input name="dein_alter"/> <input type="hidden" name="aktion" value="zeigen"/> <input type="submit" value="Los!"/> </form> </body> </html>

Der neue HTML-Code aus Sicht des Browsers:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>JSP-Beispiel</title> </head> <body> <form method="post"> Name: <input name="dein_name"/> Alter: <input name="dein_alter"/> <input type="hidden" name="aktion" value="zeigen"/> <input type="submit" value="Los!"/> </form> <h2>Bisherige Eingabe</h2> <b>Name: </b> Richard Löwneherz <br/> <b>Alter:</b> 27 <br/> </body> </html>

Wir steigen auf Netbeans um

Bildnachweis für das Endesymbol: Pixabay: janf93

Aufgabe 2: Dein erstes Netbeans-Projekt

  1. Starte Netbeans und lege ein neues Java-Webprojekt an.
  2. Lösche die standardmäßig erzeugte index.html-Datei und ersetze sie durch eine Datei namens index.jsp.
  3. Kopiere den Quellcode aus Folie 7 in die JSP-Datei und lasse sie laufen.
  4. Wenn alles soweit klappt, erweitere das Formular um die zwei Felder Telefonnummer und Hobby.
  5. Ändere den Javacode danach so ab, dass keine leeren Felder in der Bestätigungsseite angezeigt werden.
Quellcode in Netbeans
Darstellung im Browser
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>JSP-Beispiel</title> </head> <body> <form method="post"> Name: <input name="dein_name"/> Alter: <input name="dein_alter"/> <br /> Telefon: <input name="dein_telefon"/> Hobby: <input name="dein_hobby"/> <input type="submit" value="Los!"/> </form> <!-- Alles zwischen <% und %> ist Javacode --> <% if (request.getMethod().equals("POST")) { %> <h2>Bisherige Eingabe</h2> <% if (!request.getParameter("dein_name").equals("")) { %> <b>Name: </b> <%= request.getParameter("dein_name") %> <br/> <% } %> <% if (!request.getParameter("dein_alter").equals("")) { %> <b>Alter:</b> <%= request.getParameter("dein_alter") %> <br/> <% } %> <% if (!request.getParameter("dein_telefon").equals("")) { %> <b>Telefon:</b> <%= request.getParameter("dein_telefon") %> <br/> <% } %> <% if (!request.getParameter("dein_hobby").equals("")) { %> <b>Hobby:</b> <%= request.getParameter("dein_hobby") %> <br/> <% } %> <% } %> </body> </html>

Mehrere Seiten aus einem Guss

Mehrere HTML-Seiten derselben Webanwendung nutzen dasselbe Layout. Alle Seiten sollen sich dabei dasselbe HTML-Grundgerüst teilen. Doppelter Quellcode soll möglichst vermieden werden.

Hierfür wird das HTML-Grundgerüst der Seite in eine eigene Datei ausgelagert. In vielen Webframeworks wird diese Datei ein Template (deutsch „Vorlage”) genannt. Anstelle des echten Inhalts beinhaltet das Template überall Platzhalter, wo später etwas eingefügt werden soll.

<%@tag pageEncoding="UTF-8"%> <!-- Definition der Platzhalter, die unten verwendet werden. --> <!-- fragment="true" bedeutet, dass ein ganzes Stück HTML-Code eingefügt wird. --> <!-- Andernfalls wird nur ein kurzer Textausschnitt ohne HTML-Code übernommen. --> <%@attribute name="title"%> <%@attribute name="body" fragment="true" %> <%@attribute name="footer" fragment="true" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <!-- Platzhalter für den Titel --> <title>${title}</title> <link rel="stylesheet" href="style.css" /> <script src="script.js"></script> </head> <body> <nav> <!-- Nochmal der Platzhalter für den Titel --> <h1>${title}</h1> <ul> <li> <a href="index.jsp">Startseite</a> </li> <li> <a href="firmenportrait.jsp">Firmenportrait</a> </li> … </ul> </nav> <main> <!-- Platzhalter für den Body-Inhalt --> <jsp:invoke fragment="body"/> </main> <footer> <ul> … </ul> <!-- Platzhalter für den Footer-Inhalt --> <jsp:invoke fragment="footer"/> </footer> </body> </html>

Der tatsächliche Inhalt, der in das Template eingefügt wird, kommt aus einer anderen Datei. Es handelt sich dabei um die eigentliche HTML/JSP-Datei, die der Anwender gerade abrufen will. Sie beinhaltet einen Verweis auf das zu verwendende Template, so dass der Server weiß, dass er erst beide Dateien „übereinanderlegen” muss, um die fertige Seite zu erzeugen. 📄📑

<!-- Verweis auf das Verzeichnis, in dem unser Template liegt --> <%@taglib prefix="template" tagdir="/WEB-INF/tags/templates" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!-- Aufruf des Templates mit dem Namen „base.tag“ --> <template:base> <!-- Inhalt für die Titel-Platzhalter --> <jsp:attribute name="title">Startseite</jsp:attribute> <!-- Inhalt für den Body-Platzhalter --> <jsp:attribute name="body"> <h1>Ihr perfekter Feierabend!</h1> <img src="feierabend.jpg" alt="" /> <p> Kommen Sie nach einem anstrengenden Arbeitstag nach Hause und lassen Sie sich von unseren Premiumbieren verwöhnen. Es gibt nichts besseres, um den Tag ausklingen zu lassen … </p> </jsp:attribute> <!-- Inhalt für den Footer-Platzhalter --> <jsp:attribute name="footer"> © 2017 Liquid Spirit Webdesign Ltd. <br /> Im Land wo Hopfen und Malz fließt </jsp:attribute> </template:base>

Was einmal geht, geht auch zweimal. 💕 Das HTML-Template kann natürlich für beliebig viele Seiten verwendet werden, die dadurch alle ein einheitliches Layout bekommen.

<%@taglib prefix="template" tagdir="/WEB-INF/tags/templates" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <template:base> <jsp:attribute name="title">Firmenportrait</jsp:attribute> <jsp:attribute name="body"> <h2>Alles begann als kleines Familienunternehmen</h2> <p> „Wir sollten aufhören, weniger zu trinken”. Dies war stets das Motto von Julius Sinner, bevor er 1873 seine erste kleine Brauerei gründete. Dabei hatte er nur ein äußerst bescheidenes Startkapital und der Erfolg war keinesfalls gewiss … </p> </jsp:attribute> <jsp:attribute name="footer"> © 2017 Liquid Spirit Webdesign Ltd. <br /> Im Land wo Hopfen und Malz fließt </jsp:attribute> </template:base>

Aufgabe 3: Hallo Welt mal drei

Kopiere die folgenden Java Server Pages in eine neues Java-Webprojekt und schaue, ob du es zum Laufen bekommst. Anschließend versuche, so viel HTML-Code wie möglich in ein gemeinsames Template auszulagern. Das Template sollte dabei den Dateinamen base.tag haben und im Verzeichnis WEB-INF/tags/templates liegen.

index.jsp

ehrenamt.jsp

santa.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Startseite</title> <style> html, body { height: 100%; } html { font-family: sans-serif; font-size: 12pt; } h1 { color: crimson; } @media (min-width: 50em) { html { background-image: url(https://unsplash.it/1024/768/?image=1073); background-repeat: no-repeat; background-position: center center; background-size: cover; box-sizing: border-box; padding-top: 2em; padding-bottom: 2em; } main { width: 40em; margin: 0 auto; background: white; border: 1px solid #E5E5E5; box-shadow: 1px 1px 2px #BFBFBF; padding: 1em; } } </style> </head> <body> <main> <h1>Startseite</h1> <ul> <li> <a href="ehrenamt.jsp">Nur kein Ehrenamt</a> </li> <li> <a href="santa.jsp">Is Santa Claus A Sysadmin?</a> </li> </ul> </main> </body> </html>
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Nur kein Ehrenamt</title> <style> html, body { height: 100%; } html { font-family: sans-serif; font-size: 12pt; } h1 { color: crimson; } @media (min-width: 50em) { html { background-image: url(https://unsplash.it/1024/768/?image=1073); background-repeat: no-repeat; background-position: center center; background-size: cover; box-sizing: border-box; padding-top: 2em; padding-bottom: 2em; } main { width: 40em; margin: 0 auto; background: white; border: 1px solid #E5E5E5; box-shadow: 1px 1px 2px #BFBFBF; padding: 1em; } } </style> </head> <body> <main> <h1>Nur kein Ehrenamt</h1> Willst Du froh und glücklich leben, <br /> laß kein Ehrenamt dir geben! <br /> Willst du nicht zu früh ins Grab <br /> lehne jedes Amt gleich ab! <br /> <br /> Wieviel Mühen, Sorgen, Plagen <br /> wieviel Ärger mußt Du tragen; <br /> gibst viel Geld aus, opferst Zeit - <br /> und der Lohn? Undankbarkeit! <br /> <br /> Ohne Amt lebst Du so friedlich <br /> und so ruhig und so gemütlich, <br /> Du sparst Kraft und Geld und Zeit, <br /> wirst geachtet weit und breit. <br /> <br /> So ein Amt bringt niemals Ehre, <br /> denn der Klatschsucht scharfe Schere <br /> schneidet boshaft Dir, schnipp-schnapp, <br /> Deine Ehre vielfach ab. <br /> <br /> Willst du froh und glücklich leben, <br /> laß kein Ehrenamt dir geben! <br /> Willst du nicht zu früh ins Grab <br /> lehne jedes Amt gleich ab! <br /> <br /> Selbst Dein Ruf geht Dir verloren, <br /> wirst beschmutzt vor Tür und Toren, <br /> und es macht ihn oberfaul <br /> jedes ungewaschne Maul! <br /> <br /> Drum, so rat ich Dir im Treuen: <br /> willst Du Weib (Mann) und Kind erfreuen, <br /> soll Dein Kopf Dir nicht mehr brummen, <br /> laß das Amt doch and'ren Dummen. <br /> </main> </body> </html>
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Is Santa Claus A Sysadmin?</title> <style> html, body { height: 100%; } html { font-family: sans-serif; font-size: 12pt; } h1 { color: crimson; } @media (min-width: 50em) { html { background-image: url(https://unsplash.it/1024/768/?image=1073); background-repeat: no-repeat; background-position: center center; background-size: cover; box-sizing: border-box; padding-top: 2em; padding-bottom: 2em; } main { width: 40em; margin: 0 auto; background: white; border: 1px solid #E5E5E5; box-shadow: 1px 1px 2px #BFBFBF; padding: 1em; } } </style> </head> <body> <main> <h1>Is Santa Claus A Sysadmin?</h1> <ol> <li> Santa is bearded, corpulent, and dresses funny. </li> <li> When you ask Santa for something, the odds of receiving what you wanted are infinitesimal. </li> <li> Santa seldom answers your mail. </li> <li> Santa doesn't care about your deadlines. </li> <li> Your parents ascribed supernatural powers to Santa, but did all the work themselves. </li> <li> Nobody knows who Santa has to answer to for his actions. </li> <li> Santa laughes entirely too much. </li> <li> Santa thinks nothing of breaking into your $HOME. </li> <li> Only a lunatic says bad things about Santa in his presence </li> </ol> </main> </body> </html>

WEB-INF/tags/templates/base.tag

<%@tag pageEncoding="UTF-8"%> <%@attribute name="title"%> <%@attribute name="body" fragment="true" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>${title}</title> <style> html, body { height: 100%; } html { font-family: sans-serif; font-size: 12pt; } h1 { color: crimson; } @media (min-width: 50em) { html { background-image: url(https://unsplash.it/1024/768/?image=1073); background-repeat: no-repeat; background-position: center center; background-size: cover; box-sizing: border-box; padding-top: 2em; padding-bottom: 2em; } main { width: 40em; margin: 0 auto; background: white; border: 1px solid #E5E5E5; box-shadow: 1px 1px 2px #BFBFBF; padding: 1em; } } </style> </head> <body> <main> <h1>${title}</h1> <jsp:invoke fragment="body"/> </main> </body> </html>

index.jsp

<%@taglib prefix="template" tagdir="/WEB-INF/tags/templates" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <template:base> <jsp:attribute name="title">Startseite</jsp:attribute> <jsp:attribute name="body"> <ul> <li> <a href="ehrenamt.jsp">Nur kein Ehrenamt</a> </li> <li> <a href="santa.jsp">Is Santa Claus A Sysadmin?</a> </li> </ul> </jsp:attribute> </template:base>

ehrenamt.jsp

<%@taglib prefix="template" tagdir="/WEB-INF/tags/templates" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <template:base> <jsp:attribute name="title">Nur kein Ehrenamt</jsp:attribute> <jsp:attribute name="body"> Willst Du froh und glücklich leben, <br /> laß kein Ehrenamt dir geben! <br /> Willst du nicht zu früh ins Grab <br /> lehne jedes Amt gleich ab! <br /> <br /> Wieviel Mühen, Sorgen, Plagen <br /> wieviel Ärger mußt Du tragen; <br /> gibst viel Geld aus, opferst Zeit - <br /> und der Lohn? Undankbarkeit! <br /> <br /> Ohne Amt lebst Du so friedlich <br /> und so ruhig und so gemütlich, <br /> Du sparst Kraft und Geld und Zeit, <br /> wirst geachtet weit und breit. <br /> <br /> So ein Amt bringt niemals Ehre, <br /> denn der Klatschsucht scharfe Schere <br /> schneidet boshaft Dir, schnipp-schnapp, <br /> Deine Ehre vielfach ab. <br /> <br /> Willst du froh und glücklich leben, <br /> laß kein Ehrenamt dir geben! <br /> Willst du nicht zu früh ins Grab <br /> lehne jedes Amt gleich ab! <br /> <br /> Selbst Dein Ruf geht Dir verloren, <br /> wirst beschmutzt vor Tür und Toren, <br /> und es macht ihn oberfaul <br /> jedes ungewaschne Maul! <br /> <br /> Drum, so rat ich Dir im Treuen: <br /> willst Du Weib (Mann) und Kind erfreuen, <br /> soll Dein Kopf Dir nicht mehr brummen, <br /> laß das Amt doch and'ren Dummen. <br /> </jsp:attribute> </template:base>

santa.jsp

<%@taglib prefix="template" tagdir="/WEB-INF/tags/templates" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <template:base> <jsp:attribute name="title">Is Santa Claus A Sysadmin?</jsp:attribute> <jsp:attribute name="body"> <ol> <li> Santa is bearded, corpulent, and dresses funny. </li> <li> When you ask Santa for something, the odds of receiving what you wanted are infinitesimal. </li> <li> Santa seldom answers your mail. </li> <li> Santa doesn't care about your deadlines. </li> <li> Your parents ascribed supernatural powers to Santa, but did all the work themselves. </li> <li> Nobody knows who Santa has to answer to for his actions. </li> <li> Santa laughes entirely too much. </li> <li> Santa thinks nothing of breaking into your $HOME. </li> <li> Only a lunatic says bad things about Santa in his presence </li> </ol> </jsp:attribute> </template:base>

Von Servlets und Java Server Pages

Bildnachweis: Pixabay: rawpixel

Was der Server bei einer Anfrage macht

Und nun das Ganze mit Java

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Dies ist ein Minimalbeispiel für ein Servlet, das auf HTTP-Anfragen reagiert. */ @WebServlet( urlPatterns = { "/", "/hallo/*", } ) public class MinimalesServlet extends HttpServlet { /** * Diese Methode reagiert auf GET-Anfragen. */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Datenstrom für Ausgabe zum Client response.setContentType("text/html"); PrintWriter toClient = response.getWriter(); // HTML-Code erzeugen und ausgeben toClient.println("<html>"); toClient.println(" <body>"); toClient.println(" <h1>Hallo Servlet!</h1>"); toClient.println(" </body>"); toClient.println("</html>"); toClient.flush(); } }

Aufgabe 4: Dein erstes Servlet

Lege in Netbeans ein neues Java-Webprojekt an und probiere folgende Sachen aus:

  1. Lösche die Datei index.html aus dem Ordner Web Pages.
  2. Stattdessen lege eine neue Klasse an und kopiere den Quellcode aus Folie 18 hinein.
  3. Starte die Anwendung und rufe das Servlet im Browser auf. Es sollte die Meldung „Hallo Servlet!” erscheinen.
  4. Schreibe ein zweites Servlet, das auf die URL /bye/ reagiert und „Auf Wiedersehen!” anzeigt.

Die wichtigsten Servlet-Methoden

HttpServlet: Das Servlet selbst

public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletEcxeption

Diese Methode kannst du überschreiben, wenn du wirklich auf alle HTTP-Anfragen reagieren willst, egal welches Verb dabei verwendet wurde.

public void doGet(HttpServletRequest request, HttpServletResponse response) … public void doPut(HttpServletRequest request, HttpServletResponse response) … public void doPost(HttpServletRequest request, HttpServletResponse response) … public void doDelete(HttpServletRequest request, HttpServletResponse response) …

Meistens ist es jedoch besser, eine dieser Methoden zu überschreiben. Denn dadurch kannst du gezielt festlegen, wie sich das Servlet bei verschiedenen HTTP-Verben verhalten soll.

/** * Dieses Beispiel zeigt die Verwendung der verschiedenen doXXX-Methoden. */ @WebServlet(urlPatterns = {"/"}) public class GetPostServlet extends HttpServlet { /** * Wir haben eine GET-Anfrage empfangen. */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { … } /** * Wir haben eine POST-Anfrage empfangen. */ @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { … } }

HttpServletRequest: Die HTTP-Anfrage

public String getRequestURI()

Liefert den Pfad der URL, so wie er in der HTTP-Anfrage geschickt wurde. Der String beinhaltet daher auch den Namen der Webanwendung, mit dem ja jeder URL beginnt (siehe Screenshot).

public String getQueryString()

Liefert die Anfrageparameter aus der URL oder null, falls die URL keine Anfrageparameter enthält.

public String getParameter(String name)

Liefert den Wert eines einzelnen Anfrageparameters aus der URL. Alternativ kann damit auch der Wert eines Formularfelds ausgelesen werden, wenn ein HTML-Formular abgeschickt wurde.

public String getHeader(String name)

Liefert den Wert eines einzelnen Header Fields aus der HTTP-Anfrage oder null, wenn es das Feld nicht gibt.

public HttpSession getSession()

Liefert den sogenannten Session Kontext zurück. Diesen kannst du als Notizblock nutzen 🗒️, um dir über eine Anfrage hinaus benutzerspezifische Werte zu merken.

public RequestDispatcher getRequestDispatcher(String URL)

Dieses Monster benötigen wir später, um die Bearbeitung einer HTTP-Anfrage an eine Java Server Page weiterzugeben. 👾

/** * Dieses Beispiel zeigt den Aufruf einiger wichtiger Methoden * von HttpServletRequest. */ @WebServlet(urlPatterns = {"/"}) public class RequestMethodenServlet extends HttpServlet { /** * Wir haben eine GET-Anfrage empfangen. */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Informationen aus der HTTP-Anfrage auslesen String uri = request.getRequestURI(); String query = request.getQueryString(); String vornameParameter = request.getParameter("vorname"); String userAgent = request.getHeader("user-agent"); // Antwort senden response.setContentType("text/plain"); PrintWriter toClient = response.getWriter(); toClient.println("Folgende Anfrage hast du geschickt"); toClient.println("=================================="); toClient.println(""); toClient.println("URL: " + uri); toClient.println("Query String: " + query); toClient.println("Parameter vorname: " + vornameParameter); toClient.println("User Agent: " + userAgent); toClient.flush(); } }

HttpServletResponse: Die HTTP-Antwort

public void setStatus(int code) public void sendError(int code)

Setzt den Statuscode der HTTP-Antwort. Für jeden Statuscode gibt eine entsprechende Konstante, zum Beispiel response.SC_NOT_FOUND. Im Gegensatz zu setStatus() schickt sendError() die Antwort mit einer Standardfehlerseite sofort ab.

public void sendRedirect(String url)

Sendet den Stautscode 302 Redirect an den Browser und fordert ihn damit auf, eine neue Seite zu laden.

void setContentType(String type)

Setzt das Header Field content-type, um dem Browser mitzuteilen, welches Format die zurückgelieferten Daten haben. Der Wert type muss einem gültigen MIME-Type entsprechen.

public PrintWriter getWriter()

Liefert einen PrintWriter zurück, mit dem der Response Body geschrieben werden kann. Wichtig: Sobald du anfängst, etwas in den Datenstrom zu schreiben, kannst du keine anderen Methoden von HttpServletResponse mehr aufrufen, da dadurch der Response Header abgeschlossen wird.

/** * Dieses Beispiel leitet den Aufrufer immer an Google weiter. */ @WebServlet(urlPatterns = {"/"}) public class RedirectServlet extends HttpServlet { /** * Wir haben eine GET-Anfrage empfangen. */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.sendRedirect("https://www.google.de"); } }

Beispiel: Formulareingaben verarbeiten

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Dieses Servlet zeigt, wie du Formulareingaben richtig behandelt werden. * Es zeigt, dass bei einer GET-Anfrage das Formular geschickt wird und * dieses seine Daten per POST an den Server schickt. Bei einer POST-Anfrage * müssen daher die Formulardaten ausgelesen und verarbeitet werden, woraufhin * der Browser durch einen Redirect zum Neuladen der Seite gezwungen wird. */ @WebServlet(urlPatterns = {"/formular/"}) public class FormularServlet extends HttpServlet{ /** * GET-Anfrage: Liefert eine HTML-Seite mit einem Formular */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Anfang der HTML-Seite response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); PrintWriter toClient = response.getWriter(); toClient.println("<!DOCTYPE html>"); toClient.println("<html>"); toClient.println(" <head>"); toClient.println(" <meta charset='utf-8' />"); toClient.println(" <title>Hallo-Welt-Formular</title>"); toClient.println(" </head>"); toClient.println(" <body>"); // Hier nun das eigentliche Formular toClient.println(" <form method='POST'>"); toClient.println(" Wie heißt du?"); toClient.println(" <input name='vorname' type='text' />"); toClient.println(" <input type='submit' value='Abschicken' />"); toClient.println(" </form>"); // Zuletzt eingegebener Vorname, falls vorhanden // Der Wert wird in der doPost()-Methode im Session Kontext abgelegt HttpSession session = request.getSession(); String vorname = (String) session.getAttribute("vorname"); if (vorname != null) { toClient.println(" <p>"); toClient.println(" Hallo, " + vorname + "!"); toClient.println(" </p>"); } // Abschluss der Seite toClient.println(" </body>"); toClient.println("</html>"); toClient.flush(); } /** * POST-Anfrage: Verarbeitet die Formulareingaben und zwingt den Browser * danach, die Seite neuzuladen. */ @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Eingebenen Vornamen auslesen String vorname = request.getParameter("vorname"); // Vornamen im Session Kontext speichern HttpSession session = request.getSession(); session.setAttribute("vorname", vorname); // Browser zwingen, die Seite mit einem GET neuzuladen response.sendRedirect(request.getRequestURI()); } }

Der HTML-Code aus Sicht des Browsers nach dem ersten Aufruf. In den Entwicklerwerkzeugen wurde nur eine GET-Anfrage aufgezeichnet.

<!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <title>Hallo-Welt-Formular</title> </head> <body> <form method='POST'> Wie heißt du? <input name='vorname' type='text' /> <input type='submit' value='Abschicken' /> </form> </body> </html>

Der neue HTML-Code aus Sicht des Browsers. In den Entwicklerwerkzeugen sieht man schön, wie erst ein POST und dann ein GET an den Server gesendet wird.

<!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <title>Hallo-Welt-Formular</title> </head> <body> <form method='POST'> Wie heißt du? <input name='vorname' type='text' /> <input type='submit' value='Abschicken' /> </form> <p> Hallo, Alf! </p> </body> </html>

Aufgabe 5: Ein kleines Servlet-Quiz

Aufgabe 5.1: Grundlagen

1. Wie werden Request Handler in Java allgemein genannt?

  1. HTTP Handler
  2. Java Server Pages
  3. Servlets

2. Auf welche zwei Arten kann das URL-Routing in Java definiert werden?

  1. Mit der Annotation @WebServlet
  2. Mit der Annotation @UrlPatterns
  3. Im Konstruktor eines Servlets
  4. In der Datei WEB-INF/web.xml

3. Welchen Zweck erfüllen die Request Handler einer Webanwendung?

  1. Sie implementieren verschiedene Protokolle wie SFTP oder IMAP
  2. Sie nehmen HTTP-Anfragen entgegen und erzeugen eine HTTP-Antwort
  3. Sie verarbeiten die vom Server empfangene Daten mit JavaScript

4. Welchen Zweck erfüllen Templates in einer Webanwendung

  1. Sie sollen die serverseitige Erzeugung von HTML-Code vereinfachen
  2. Sie sollen die Verarbeitung komplexer HTTP-Anfragen ermöglichen
  3. Sie dienen als Vorlage für neue Webanwendungen

5. Was ist das Post/Redirect/Get-Pattern?

  1. Eine spezielles Kontaktformular auf einer Firmenwebseite
  2. Ein empfohlenes Vorgehen bei der Weiterentwicklung alter Servletklassen
  3. Ein Schema, wie Formulareingaben sinnvoll behandelt werden können

6. Welche Aufgabe hat der Session Kontext

  1. Daten im Browser zwischenspeichern, um sie später an den Server zu schicken
  2. Serverseitig je Benutzer bestimmte Daten über mehrere Anfragen hinweg zwischenspeichern
  3. Serverseitig über alle Benutzer hinweg globale Daten zwischenspeichern

Aufgabe 5.2: Wichtige Methoden

1. Wie heißt die Methode eines Servlets, die auf alle HTTP-Anfragen reagiert?

  1. doAll()
  2. service()
  3. handle()

2. Welche beiden Parameter bekommen die do…-Methoden eines Servlets übergeben?

  1. Ein HttpRequest- und ein HttpResponse-Objekt
  2. Zwei Datenströme zum Lesen und Schreiben der HTTP-Anfrage bzw. Antwort
  3. Ein HttpServletResponse- und ein HttpServletRequest-Objekt
  4. Ein HttpServletRequest- und ein HttpServletResponse-Objekt

3. Mit welcher Methode kann die angefragte URL ermittelt werden?

  1. request.getRequestURI()
  2. response.getRequestURI()
  3. this.getRequest().getURI()

4. Mit welchen Methoden kann der Statuscode der HTTP-Antwort gesetzt werden?

  1. response.setStatus()
  2. response.sendError()
  3. response.setStatusCode()
  4. response.getResponse().setStatus()

5. Mit welcher Methode erhält man einen Datenstrom zum Schreiben des HTTP Response Body?

  1. request.response.getWriter()
  2. request.getInputStream()
  3. response.setOutputStream()
  4. response.getWriter()
  5. response.getBodyStream()

6. Mit welcher Methode kann bekommt man Zugriff auf den Session Kontext?

  1. this.getSessionContext()
  2. this.getSession()
  3. request.getSession()
  4. request.session.get()

Lösung: Aufgabe 5.1: 3, 1+4, 2, 1, 3, 2. Aufgabe 5.2: 2, 4, 1, 1+2, 4, 3.

Weiterleitung an eine Java Server Page

So sieht das Netbeans-Projekt aus. Wenn du es nachbauen willst, musst du exakt die Verzeichnisstruktur einhalten.

Dies ist das Servlet, dass bei jeder GET- und POST-Anfrage durchlaufen wird.

import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Dieses Servlet zeigt, wie die Erzeugung des HTML-Codes an eine Java Server * Page ausgelagert werden kann. */ @WebServlet(urlPatterns = {"/index.html"}) public class HalloServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Zwischenspeichern eines Wertes, den wir in der JSP anzeigen wollen // Die letzte Zeile ist die wichtige Zeile hier DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); Date date = new Date(); String tagesdatum = dateFormat.format(date); request.setAttribute("tagesdatum", tagesdatum); // Anfrage an eine JSP weiterleiten, um damit den HTML-Code // der Seite zu generieren RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/hallo.jsp"); dispatcher.forward(request, response); // Werte im Session Kontext entfernen, damit wir beim nächsten mal // wieder von vorne anfangen HttpSession session = request.getSession(); session.removeAttribute("vorname"); session.removeAttribute("nachname"); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Eingegebene Werte auslesen String vorname = request.getParameter("vorname"); String nachname = request.getParameter("nachname"); // Werte im Session Kontext ablegen HttpSession session = request.getSession(); session.setAttribute("vorname", vorname); session.setAttribute("nachname", nachname); // Und die Seite nochmal laden lassen response.sendRedirect(request.getRequestURI()); } }

Innerhalb des Servlets wird nun kein HTML-Code mehr erzeugt. Das macht die folgende JSP für uns. Die Datei muss WEB-INF/hallo.jsp heißen!

<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Hallo, Welt!</title> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="container"> <!-- Eingabefelder, nur vor dem Abschicken anzeigen --> <c:if test="${vorname == null}"> <h1>Wie heißt du?</h1> <form method="POST"> <input name="vorname" type="text" placeholder="Vorname" /> <input name="nachname" type="text" placeholder="Nachname" /> <input type="submit" value="Abschicken" /> </form> </c:if> <!-- Begrüßung, nur nach dem Abschicken anzeigen --> <c:if test="${vorname != null}"> <h1>Hallo, ${vorname} ${nachname}!</h2> <h2>Sei gegrüßt!</h2> <a href="">Nochmal</a> </c:if> <!-- Aktuelles Datum immer anzeigen --> <p> <small> Heute ist der ${tagesdatum}. </small> </p> </div> </body> </html>

Dieses mal gönnen wir uns auch ein Stylesheet, damit es ordentlich aussieht. Die Datei muss style.css heißen.

html, body { font-family: sans-serif; font-size: 12pt; height: 100%; margin: 0; padding: 0; } html { background-image: url(https://picsum.photos/1024/?image=1067); background-repeat: no-repeat; background-position: center center; background-size: cover; } body { display: flex; justify-content: center; align-items: center; } .container { background: rgba(255,255,255,0.8); border-radius: 0.5em; padding: 1em; margin: 2em; } h1, h2 { color: crimson; text-shadow: 0 0 1px rgba(0,0,0,0.5); margin: 0 0 0.5em 0; } h2 { color: deepskyblue; } input[type="text"] { display: block; width: 25em; box-sizing: border-box; border: 1px solid grey; margin-bottom: 0.5em; }

Und so sieht die Anwendung aus, wenn sie fertig ist.

Aufgabe 6: Formularprüfungen ergänzen

Aufgabe

Kopiere den Quellcode aus der vorherigen Folie in ein neues Netbeansprojekt und nimm folgende Änderungen daran vor:

  • Wie immer, lösche die von Netbeans automatisch erzeugte Datei index.html.
  • Zunächst soll in doPost() geprüft werden, ob beide Felder ausgefüllt wurden.
  • Sind die Eingaben okay, soll die bekannte Bestätigungsseite angezeigt werden.
  • Fehlt eines der beiden Felder, soll das Formular mit einer Fehlermeldung stehen bleiben.
  • Achte dabei darauf, dass die bisherigen Eingaben nicht verloren gehen!
  • Eine Prüfung mit JavaScript soll nicht erfolgen. Diese sparen wir uns hier. 🙂

Hinweise

  • Du musst ein Kennzeichen im Session Kontext ablegen, ob die Prüfung erfolgreich war.
  • Wird ein Wert nicht gefunden, liefert die Expression Language immer null zurück.
  • Wenn du eine Liste mit Fehlermeldungen im Session Kontext hast, kannst du sie wie folgt ausgeben:

    <c:forEach items="${fehlermeldungen}" var="fehlermeldung"> ${fehlermeldung} </c:forEach>

doPost()-Methode im Servlet

Die neue doPost()-Methode sieht wie folgt aus. Neu hinzugekommen sind die beiden Attribute fehlermeldungen und ergebnisAnzeigen im Session Kontext.

@Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Eingegebene Werte auslesen List<String> fehlermeldungen = new ArrayList<>(); String vorname = request.getParameter("vorname"); String nachname = request.getParameter("nachname"); if (vorname == null || vorname.isEmpty()) { fehlermeldungen.add("Gib erst einen Vornamen ein"); } if (nachname == null || nachname.isEmpty()) { fehlermeldungen.add("Gib erst einen Nachnamen ein"); } // Werte im Session Kontext ablegen HttpSession session = request.getSession(); session.setAttribute("vorname", vorname); session.setAttribute("nachname", nachname); session.setAttribute("fehlermeldungen", fehlermeldungen); session.setAttribute("ergebnisAnzeigen", fehlermeldungen.isEmpty()); // Und die Seite nochmal laden lassen response.sendRedirect(request.getRequestURI()); }

Java Server Page

Die Java Server Page muss wie folgt angepasst werden:

<!-- Eingabefelder, nur vor dem Abschicken anzeigen --> <c:if test="${ergebnisAnzeigen != true}"> <h1>Wie heißt du?</h1> <form method="POST"> <input name="vorname" type="text" placeholder="Vorname" value="${vorname}" /> <input name="nachname" type="text" placeholder="Nachname" value="${nachname}" /> <input type="submit" value="Abschicken" /> </form> <ul class="error"> <c:forEach items="${fehlermeldungen}" var="fehlermeldung"> <li>${fehlermeldung}</li> </c:forEach> </ul> </c:if> <!-- Begrüßung, nur nach dem Abschicken anzeigen --> <c:if test="${ergebnisAnzeigen == true}"> … </c:if>

Stylesheet

Im Stylesheet muss eine neue Klasse für Fehlermeldungen definiert werden:

.error { color: red; font-weight: bold; }

Hinweise zum Schluss

Bildnachweis: Pixabay: rawpixel

Do & Don't

Allgemeine Hinweise

Java gegen den Rest der Welt

Besonderheiten von Java

Rechtshinweise

Creative Commons Namensnennung 4.0 International

§