Servlets
Servlets sind Javaklassen, die nach den Vorgaben der Servletspezifikation implementiert sind und in einem Servlet-Container leben. Sie nehmen Anfragen von HTTP-Clients entgegen und beantworten diese in Einklang mit der HTTP-Spezifikation. Eigene Funktionalität wird in erster Linie durch das Überschreiben der Servicemethoden der Servlet-Stubklassen erreicht.
Servlet Lifecycle
Request Parameter
Form-Parameter oder URL-Parameter vom Request lesen:
req.getParameter("name"); // liefert den Parameter diese Namens
req.getParameterNames(); // liefert alle Parameternamen
In einer Form können jedoch auch mehrere Parameter mit dem gleichen Namen exisiteren, diese werden dann als String-Array geliefert:
req.getParameterValues("name"); // ein String[] mit allen Parametern dieses Namens
req.getParameterMap(); // eine Map mit key-String und value-String[]
HTTP Header
Auslesen des Request Header:
req.getHeader("name");
req.getHeaderNames();
Auch hier kann zu einem Header mehr als ein Eintrag existieren, dann hilft
req.getHeaders("name"); // liefert eine Enumeration aller Headereinträge zu diesem Namen
Für ganze Zahlen und Datumsangaben gibt es typsichere Methoden zum Auslesen:
req.getIntHeader("name"); // liefert einen Header als int
req.getDateHeader("name"); // liefert einen Header als Date
Header können im Response auch gesetzt werden. Viele davon werden dabei automatisch (vom GenericServlet) gesetzt:
// setzt eine Wert im Header, der vorhandene Wert wird überschrieben
resp.setHeader("name", "value");
resp.setDateHeader("name", 12345678);
resp.setIntHeader("name", 666);
// fügt einen Wert dem Header hinzu
resp.addHeader("name", "value");
resp.addDateHeader("name", 12345678);
resp.addIntHeader("name", 666);
Cookies
Auslesen von Cookies aus dem Request
req.getCookies(); // liefert ein Cookie-Array mit allen Cookies des RequestsSetzen von Cookies im Response:
resp.addCookie(cookie); // ein Cookie wird dem Response hinzugefügt // Ein Cookie wird erzeugt Cookie cookie = new Cookie(name, value); cookie.setMaxAge(seconds); cookie.getValue(name); // TODO stimmt das?Bei der Arbeit mit Cookies muss man sich allerdings an gewisse Limitierungen halten, hier ein paar konkrete Zahlen:
Content Type
Der Content Type informiert den anfragenden Client über die Natur des Inhalts, der im Response geschickt wird. Setzen des Content Type am Response:
resp.setContentType("text/html;charset=UTF-8");
resp.setContentType("application/pdf");
// resp.setContentType("application/java");
// resp.setContentType("application/jar");
:
Response Streams
Responses können formatiert (characterbasiert) oder unformatiert (bytebasiert) erfolgen. Beispiel: Für einen Fileupload wird (1) zunächst ein PrintWriter besorgt. Als nächstes setzt man Contenttype (2), Cache-Optionen (3) und Filename (4). Dann wird ein char-Stream auf den PrintWriter geschrieben (5).
1 PrintWriter out = response.getWriter();
// für den Browser unbekannter Datentyp (verhindert ungewolltes Öffnen)
2 response.setContentType("application/x-download");
// Der Name, unter dem clientseitig abgespeichert werden soll
3 response.setHeader("Content-Disposition", "attachment; filename=myName.txt");
// siehe Cache-Kapitel
4 response.setDateHeader("Expires", System.currentTimeMillis( ) + someTimes);
5 Reader in = new BufferedReader(new FileReader(myFile));
char[] buf = new char[4096];
int r;
while ((r = in.read(buf)) != -1) {
out.write(buf, 0, r);
}
Für unformatierte Daten dann statt des PrintWriter ein OutputStream benutzen:
1 OutputStream out = response.getOutputStream();
// für den Browser hoffentlich bekannter Datentyp
2 response.setContentType("application/zip");
// Der Name, unter dem clientseitig abgespeichert werden soll
3 response.setHeader("Content-Disposition", "attachment; filename=myName.txt");
// siehe Cache-Kapitel
4 response.setDateHeader("Expires", System.currentTimeMillis() + someMoreMillis);
5 InputStream in = getServletContext().getResourceAsStream("/foobar.zip");
int r = 0;
byte[] chunk = new byte[1024];
while ((r = in.read(chunk)) != -1) {
out.write(chunk, 0, r);
}
Request Character Encoding
Bei GET und DELETE Requests werden die URL und HTTP Header übertragen, dank HTTP Spezifikation müssen sich Client und Server dabei nicht über das verwendete Encoding dieser Daten austauschen, denn die HTTP Spec regelt das. Bei POST und PUT Requests jedoch gibt es neben der URL und dem Header noch Nutzdaten, deren Encoding der Client bestimmt. Dieses Encoding muss der Server beim Lesen dieser Daten (also beim Lesen der Request-Parameter) kennen, damit deren Inhalt korrekt interpretiert wird. Das Attribut enctype in der HTML Form regelt dies nicht. Es bestimmt lediglich wie die Inhalte der Form als POST Request verpackt werden und wird hier nicht diskutiert.
Intuitiv erwartet man also, dass ein HTTP Client verpflichtet ist, den Server über das Encoding der Request-Parameter zu informieren. In der HTTP Spezifikation war nichts in dieser Richtung zu finden. Hier soll es deshalb darum gehen, wie Web Container mit diesem Problem umgehen und wie der Anwendungsentwickler den Ablauf steuern kann:
Der Anwendungsentwickler muss sich anders behelfen. Im ServletRequest kann er die Methode
ServletRequest.getCharacterEncoding()rufen. Wenn diese null zurückgibt, dann hat der Container kein Encoding ermitteln können - der Client hat keinen Content-Type Header gesetzt. In diesem Fall kann man das Encoding mit
ServletRequest.setCharacterEncoding("UTF-8")
explizit setzen. Eingesetzt wird das gleiche Encoding, das auf der die FORM beinhaltende HTML Seite benutzt wurde. In den meisten Fällen kennt das der Entwickler, weil er es selbst bestimmt hat. Leider kann man das Encoding nicht in der FORM, zum Beispiel als Hidden Field, definieren: setCharacterEncoding muss VOR dem Lesen der Request-Parameter erfolgen, sonst ist es wirkungslos. Nachdem man also ein hypothetisches Hidden Field mit dem Encoding Angaben gelesen hat, kann man es nicht mehr ändern. Schade eigentlich.
Oftmals ist es notwendig, den vom Servlet generierten Response zu manipulieren. Ein populäres Beispiel ist die Kompression des Outputs bevor er zum Client gesendet wird. Das HttpServletResponse Objekt repräsentiert in letzter Instanz eine Strom auf einen Socket, es ist im allgemeinen dann aus Sicht des Filters zu spät für die Manipulation dieses Stroms. Es muss also eine eigene Implementierung des HttpServletResponse Interfaces übergeben werden, welches keinen Socket dekoriert. Diese Implementierung wird erleichtert Dank der Wrapperklassen:
HttpServletRequestWrapper ServletRequestWrapper HttpServletResponseWrapper ServletResponseWrapperIn ihrem Konstruktor übernehmen sie das originale Request/Response Objekt und delegieren alle Methoden an dieses "gewrappte" Objekt weiter. Sie funktionieren also exakt wie das Original (Decorator-Pattern). In einer eigenen Implementierung erbt man von einer Wrapperklasse und überschreibt eine oder mehrere Methoden.