17.18 Das HttpServletResponse-Objekt
 
Das HttpServletResponse-Objekt ist die Rückrichtung vom Servlet zum Client. Bei den JSPs haben wir das implizite Objekt response, das für die Rückrichtung gedacht ist, schon kennen gelernt.
17.18.1 Wir generieren eine Web-Seite
 
Unser folgendes Beispiel führt uns noch einmal zu einer einfachen Web-Seite:
Listing 17.22
FirstServlet2.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FirstServlet2 extends HttpServlet
{
public void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
out.println( "Oho. Nun mit doGet()!" );
}
}
Das Objekt HttpServletRequest beinhaltet die Informationen vom Browser. HttpServlet-Response ist für die Ausgabe zuständig, die das Servlet liefert und zum Client schickt. Beide Klassen liegen zusammen mit HttpServlet im Paket javax.servlet.http. Das Paket javax.servlet.* wird nur wegen ServletException eingebunden. Im Paket java.io liegt die Klasse Writer. Die IOException müssen wir auffangen, da getWriter() eine Ausnahme auslösen kann.
Eine HTML-Seite statt einer Textseite erzeugen
In den ersten Beispielen haben wir kein spezielles Ausgabeformat gewählt, es war Plain-Text. Um andere Typen senden zu können, beispielsweise HTML oder Binärdaten für Bilder, müssen wir zuerst dem Browser mitteilen, um welches Ausgabeformat es sich handelt, und anschließend den Datenstrom speziell formatieren. Das Ausgabeformat wird dabei mit Hilfe von MIME-Type definiert. (MIME-Types sind in RFC 2045 definiert.) Dazu schickt das Servlet eine spezielle Kennung namens Content-Type. Da wir diese zum Browser schicken, wenden wir uns an das HttpServletResponse-Objekt. Dies bietet setHeader() an, um beliebige Header zu setzen:
response.setHeader( "Content-Type", "text/html");
Die Methode erhält als Argument zwei Zeichenketten: den Header und den dazugehörigen Wert. Da der Header Content-Type jedoch so häufig benötigt wird, bietet die Schnittstelle HttpServletResponse dafür die eigene Methode setContentType() an:
response.setContentType( "text/html" );
Um reine (Nur-)Textausgaben zu erzeugen, schreiben wir den Header text/ plain.
Nach dem Setzen folgt der zweite Schritt. Wir schreiben HTML-Text über den Ausgabestrom, nachdem der Header gesetzt ist:
PrintWriter out = response.getWriter();
out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n" +
"<HTML>\n" +
"<HEAD><TITLE>Der Kopf</TITLE></HEAD>\n" +
"<BODY>\n" +
"<H1>Das ist HTML in Perfektion</H1>\n" +
"</BODY></HTML>");
Wie wir sehen, ist es ein sehr aufwändiges Unterfangen, HTML-Code zu erzeugen. In diesem Tutorial verzichten wir meistens darauf, weil wir viele Server-Eigenschaften auch nur mit Hilfe von Text vermitteln können.
Eine wohlgeformte HTML-Datei besteht aus einer Anweisung zu Beginn, die den XML-Typ über das Tag DOCTYPE angibt. Anschließend folgen die HTML-Anweisungen. Unterschiedliche Hersteller bieten Bibliotheken an, mit denen die Generierung von Web-Seiten über spezielle Hilfsklassen einfacher wird. Der Aufbau der Seite wird dabei als baumartig organisiertes Objekt verwaltet und anschließend in HTML übersetzt. Der Content-Type sollte vor der ersten Ausgabe gesetzt sein.
17.18.2 Binärdaten senden
 
Um Binärdaten zu senden, muss lediglich der passende Typ im Content-Type eingestellt sein. Dabei kann es sich um Daten handeln, die der Browser interpretiert oder auch nicht.
Beispiel Content-Type bei Bildern.
response.setContentType( "image/jpeg" );
response.setContentType( "image/gif" );
|
Wenn wir den Inhaltstyp – wie oben geschehen – setzen und einen binären Datenstrom mit den Bildinformationen verschicken, ist schnell ein Bilder-Servlet geschrieben.
Listing 17.23
BinarySender.java
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class BinarySender extends HttpServlet
{
public void doGet( HttpServletRequest request,
HttpServletResponse response )
throws ServletException, IOException
{
String filename = "C:/WINNT/Profiles/Administrator/"+
"Desktop/wirelessduke.jpg";
InputStream in = new BufferedInputStream(
new FileInputStream(filename) );
String s = URLConnection.guessContentTypeFromStream(in);
response.setContentType( s );
byte[] pic= new byte[in.available()];
in.read( pic );
OutputStream out = response.getOutputStream();
out.write( pic );
}
}
In den Eingabestrom schreiben wir dieses Mal keine Unicode-Zeichen, sondern binäre Daten. Daher ist ein Writer nicht nötig. Stattdessen holen wir uns direkt einen OutputStream mit getOutputStream(). Falls wir uns jedoch schon vorher einen Writer mit getWriter() geholt haben, bestraft uns Tomcat mit einer netten Seite und dem Fehler 500: Es dürfen nicht gleichzeitig Writer und Stream offen sein.
Beherzigen wir dies, können wir einfach mit write() Daten schreiben, und der Client versucht, diese zu interpretieren. Der Content-Type und die Binärdaten müssen zusammenpassen, andernfalls ist das Verhalten nicht immer vorhersehbar.
Falls uns der Datentyp selber nicht klar ist, lässt sich eine kaum bekannte statische Funktion aus der Klasse URLConnection nutzen: guessContentTypeFromStream(). Sie versucht mit Hilfe der ersten Bytes auf den Inhalt zu schließen. Da die Methode einen Stream mit mark/reset benötigt (damit die Daten gelesen und wieder zurückgesetzt werden können), geben wir ihm einen markierungsfähigen Datenstrom in Form des BufferedInputStream. Findet er den Typ (wenn nicht, liefert die Methode null), wird dieser sofort in setContentType() eingesetzt. Somit können wir die Datei leicht ändern, und die Klasse sucht sich automatisch den richtigen Typ für verbreitete Binärdaten. Unter der Servlet-API gibt es auch die Methode ServletContext.getMimeType(), die das Erkennen des Typs vornimmt.
17.18.3 Noch mehr über Header, die der Server setzt
 
Bisher kennen wir von der Klasse HttpServletResponse die Methode setHeader() für beliebige Header.1
Beispiel Setze den Header pragma, damit vom Browser keine Daten im Cache gehalten werden.
response.setHeader( "pragma", "no-cache" );
|
Mit dieser Aufforderung soll der Browser die Seite jedes Mal neu laden. Das ist bei dynamischen Seiten besonders wichtig, da sie bei jedem Aufruf neu generiert werden und sich Werte ändern können, wie es zum Beispiel bei Warenkorbsystemen der Fall ist. Da wir uns als Applikationsentwickler nicht immer mit dem Namen der Header herumärgern wollen, bietet die Bibliothek einige Spezialfunktionen an.
Beispiel Für den Header Content-Type gibt es die spezielle Methode setContentType().
response.setHeader( "Content-Type", "text/html");
response.setContentType( "text/html" );
|
Daneben gibt es setContentLength(), die den Header Content-Length setzt. Diese Länge muss nicht gesetzt werden und wird automatisch berechnet. Falsche Längen könnten zu Ausnahmesituationen führen. Der Gebrauch ist jedoch nützlich, wenn vorher die gesamte Web-Seite in einem StringBuffer sb gesammelt und in einem Rutsch übertragen wird. Dann können wir setContentLength(sb.length()) aufrufen.
Um einen Datums-Header zu setzen, existiert setDateHeader(String, long). Das Argument ist eine beliebige Zeichenkette, die mit einem Datumswert verbunden wird. Das long gibt die Millisekunden seit dem 1.1.1970 an. Die erzeugte Ausgabe schreibt einen GMT-String.
Eine weitere Hilfsfunktion ist setIntHeader(), die Zahlenwerte mit Schlüsseln in den Header schreibt. Hier übernimmt die Methode die Konvertierung von String in eine Ganzzahl.
Neben diesen setXXX()-Methoden, die möglicherweise gesetzte Header überschreiben, lässt sich mit containsHeader(String) abfragen, ob Wertepaare schon gesetzt sind. Neben den setXXX()-Methoden gibt es auch entsprechende addXXX()-Methoden, die die Werte nicht überschreiben, sondern hinzufügen. Für Cookies existiert eine zusätzliche Methode namens addCookie(), die einen Cookie im Header setzt.
1 Den Header pragma gibt es schon seit HTTP 1.0. In HTTP 1.1 wurde die Cache-Fähigkeit verfeinert, etwa mit cache-control, die den Header pragma ersetzt. Dennoch sollten beide Header gesetzt werden.
|