Kategorien
Webdesign

Facebook-Anwendungen mit PHP programmieren – Quiz für Webgeeks (3)

Der nächste Schritt zur eigenen Facebook-Anwendung mit PHP dreht sich ums Herz des Workshops beziehungsweise unserer Quizanwendung – nämlich das Quiz selbst. Wir möchten den Nutzer durch eine Abfolge von Quizfragen schicken und die Antworten in einer Datenbank hinterlegen. Später soll dem Nutzer sein Ergebnis mitgeteilt werden. Dazu werden wir auf alle zentralen Elemente, die eine Facebook-Anwendung ausmachen, zurückgreifen.

Benutzersteuerung

Wir werden den Benutzer sehr primitiv durch die Anwendung führen, indem wir über den URL weitergeben, wo der Nutzer gerade steht. In diesem Artikel wurde es relativ einfach gelöst. Über einen Formular-Generator im Internet wurde das komplette Quiz in einer HTML-Datei erzeugt. Diese wird als Iframe eingebunden. Damit können Nutzer das Quiz in einem Schritt beantworten, das fertige Formular finden Sie etwa unter http://www.devmag.net/fb/form.html.

Wer es etwas komplizierter machen möchte, lässt den Benutzer von Frage zu Frage durchklicken, wobei die Antworten auf die einzelnen Teilfragen Schritt für Schritt in einer Datenbank abgelegt werden. Bei einer solchen Umsetzung kann man auch leicht eine Art Statusbalken, sprich Laufzeitmeldung, programmieren, die dem Nutzer zeigt, wo er gerade in der Anwendung steht. Ein Beispiel für eine derartige Lösung können Sie samt Code unter http://www.homepage-total.de/php/quiz.php finden.

Daten abgreifen und ablegen

Wie bereits im letzten Teil vorgestellt, können Sie die persönlichen Daten, welcher der Nutzer mit der Anwendung teilt, in der eigenen Datenbank ablegen. Es ist genau dieses, was stets von Datenschützern kritisiert wird, da manchen Anwendern nicht bewusst ist, dass die Daten an Dritte so indirekt weitergegeben werden.

Lesetipp: Wichtige Informationen zum Thema Facebook und Datenschutz finden Sie in einem separaten Dr.-Web-Beitrag (Link).

Als Beispiel werden wir lediglich den Namen und die Benutzerkennung des Facebook Nutzers abspreichern. Im letzten Teil wurde gezeigt, wie sie etwa über FQL-Abfragen noch weiteren Daten entnehmen können. Für die Illustration wird hier jedoch lediglich ein minimalistischer Datenabruf durchgeführt.

Die verwendete MySQL Tabelle kann mit folgendem Code erzeugt werden:

CREATE TABLE IF NOT EXISTS `fb_quiz` (
  `id` int(11) NOT NULL auto_increment,
  `uid` int(11) NOT NULL,
  `first_name` varchar(50) NOT NULL,
  `last_name` varchar(50) NOT NULL,
  `result` float NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `uid` (`uid`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2

Der vollständige Code der Anwendung

<?php
// die Facebook PHP Client-Library einfügen
include_once 'php/facebook.php';
// Ihr API Schlüssel
$api_key = '';
$secret  = '';
// eine Objektinstanz erzeugen
$facebook = new Facebook($api_key, $secret);
//wird verwendet, sodass die Seite den Facebook Look hat - also mit Tabnavigation
$facebook->require_frame();
//Fordert den Nutzer auf, der Applikation den Zugriff zu gewähren
$user = $facebook->require_login();
//Datenbanksetup
$connid=mysql_connect("localhost","", "") or die("Verbindungsversuch fehlgeschlagen");
mysql_select_db("devmag", $connid) or die("Konnte die Datenbank nicht waehlen.");
function DisplayRanking() {
 echo "<h2>Die Bestenliste</h2>";
 //Ranking darstellen
 $sql = "SELECT uid, result FROM `fb_quiz` ORDER BY result DESC LIMIT 10";
 if(!$erg = mysql_query($sql)) {
 echo "fehler<br/>";
 }
 $erg2 = mysql_fetch_array($erg, MYSQL_ASSOC);
 ?><br/>
  <table border="0">
  <tr><td>Nutzer</td><td>Punktzahl</td></tr>
 <?php
 if(count($erg2) >= 2 && !isset($erg2['uid']) ) {
  foreach($erg2 as $elem) {
  echo "<tr><td><fb:profile-pic uid=\"".$elem['uid']."\" size=\"square\"/></td><td>".$elem['result']."</td></tr>";
  }
 }
 else {
 echo "<tr><td><fb:profile-pic uid=\"".$erg2['uid']."\" size=\"square\"/></td><td>".$erg2['result']."</td></tr>";
echo "</table>";
}
function RetrieveData($user, $score, $facebook) {
 $fql = "SELECT last_name, first_name FROM user WHERE ".$user. " IN (uid)";
 $userDetails = $facebook->api_client->fql_query($fql);
 $sql = "INSERT INTO fb_quiz VALUES ('','".$user."','".$userDetails[0]['first_name']."','".$userDetails[0]['last_name']."','".$score."');";
 mysql_query($sql);
}
function CheckQuizTaken($user) {
$sql = "SELECT uid FROM fb_quiz WHERE uid='".$user."'";
 $num = mysql_num_rows(mysql_query($sql));
 if($num != 0) {
 return false; }
 else {
 return true;
 }
}
function FriendSelect($facebook, $user) {
//die UIDs aller Freunde ermitteln, welche die Anwendung bereits hinzugefügt haben
 $fql = "SELECT uid FROM user WHERE is_app_user = 1 AND uid IN (SELECT uid2 FROM friend WHERE uid1 = ".$user.")";
 $res = $facebook->api_client->fql_query($fql);
 //das Ergebnis der Abfrage ist in einem zweidimensionalen Array der Form $res[$i][0]['uid'], wobei $i Zählvariable ist
 $friends = "";
 //aus dem Ergebnis Array ein String machen, mit Kommatrennung, da wir das als Attribut für unser FBML Element verwenden  können
 for($i=0; $i<count($res);$i++) {
  if($i==0) {
   if(!isset($res[0]['uid'])) {
   $friends = "";
   }
   else {
   $friends = $res[0]['uid'];
   }
  }
  else {
   $friends = $friends.",".$res[$i]['uid'];
  }
 }
 $invitationContent ="<fb:name uid=\"".$user."\" firstnameonly=\"true\" shownetwork=\"false\"/> hat sich im <a  href=\"http://apps.facebook.com/cook-off/\">Webgeek Quiz</a> versucht und dachte, dass du es auch ausprobieren  solltest!\n"."<fb:req-choice url=\"".$facebook->get_add_url()."\" label=\"Put Webgeek on your profile\"/>";
 ?>
 <fb:request-form action="index.php?action=step2" method="post" type="cook-off" content="<?php echo  htmlentities($invitationContent,ENT_COMPAT,'UTF-8'); ?>">
 <fb:multi-friend-selector actiontext="Hier ist die Liste deiner Freunde, die sich noch nicht probiert haben!"  exclude_ids="<?php echo $friends; ?>" /></fb:request-form>
<?php
}
if (isset($_GET["action"])) {
 if($_GET["action"] == 'step1')
 {
  DisplayRanking();
 ?>
  <p>Hallo <fb:name uid='<?php echo $user?>' capitalize="true" firstnameonly="true" useyou="false" linked="true"/>,  bevor es richtig los geht -- lade doch deine Freunde ein.</p>
 <?php
 FriendSelect($facebook, $user);
 echo "Oder einfach <a href=\"index.php?action=step2\">hier</a> weiter.";
 }
 elseif($_GET["action"] == 'step2')
 {
 if(CheckQuizTaken($user)) {

 echo "<fb:iframe src=\"http://www.devmag.net/fb/form.html\" scrolling=\"true\" smartsize=\"true\"></fb:iframe>";
 }
 else {
 echo "<h1>Sie haben das Quiz schon beantwortet</h1>";
  DisplayRanking();
 }
 }
 elseif($_GET["action"] == 'step3')
 {
 //Formular auswerten
 $score = "0";
 if($_POST['element_1'] == "1") { $score += 1; }
 if($_POST['element_2'] == "1024") { $score += 1; }
 if($_POST['element_4'] == "1") { $score += 1; }
 if($_POST['element_5'] == "1") { $score += 1; }
 if($_POST['element_6'] == "1") { $score += 1; }
 if($_POST['element_7'] == "1") { $score += 1; }
 if($_POST['element_8'] == "1") { $score += 1; }
 if($_POST['element_9'] == "1") { $score += 1; }
 if($_POST['element_10'] == "1") { $score += 1; }
 if($_POST['element_3_3'] == "1970") { $score += 0.5; }
 if($_POST['element_3_2'] == "01") { $score += 0.25; }
 if($_POST['element_3_1'] == "01") { $score += 0.25; }
 RetrieveData($user, $score, $facebook);
 echo "<center><h1>Dein Ergebnis: ".$score." von 10 Punkten.</h1><br/>";
 DisplayRanking();
 echo "</center>";
}
}
else {
?>
<p>Hallo <fb:name uid='<?php echo $user?>' capitalize="true" firstnameonly="true" useyou="false" linked="true"/>, nimm hier an unserem Quiz teil und finde heraus, ob du ein Geek bist.</p>
<h2><a href="index.php?action=step1">Quiz starten</a></h2>
<?php
}
?>

Wichtige Stellen im Code

Für den eigentlichen Programmablauf verwenden wir über GET übermittelte Schritte. Das ist eine sehr primitive Benutzersteuerung. Wie oben bereits erwähnt, kann man dies professioneller und vor allem Hackangriffsicherer gestalten.

Das eigentliche Formular für das Quiz wird über ein Iframe eingebunden, an der Stelle

if(CheckQuizTaken($user)) {
  echo "<fb:iframe src=\"http://www.devmag.net/fb/form.html\" scrolling=\"true\" smartsize=\"true\"></fb:iframe>";
}

Dieses Seite beinhält das Quiz in einem Formular, welches die Daten dann an die Facebook Anwendung über Post übermittelt.

Das Formular selbst wird dann im dritten Schritt ausgewertet über die mit $_POST[] übermittelten Eingaben des Nutzers. Wie Sie sehen, können Sie jetzt wirklich leicht erahnen, wie sie im Quiz eine gute Punktzahl erreichen können. Das Formular selbst wurde über einen schlichten Online-Generator erzeugt.

Damit Codebestandteile mehrfach verwendet werden können, wurden vier Funktionen definiert:

  • FriendSelect($facebook, $user),
  • CheckQuizTaken($user),
  • DisplayRanking() und
  • RetrieveData($user, $score, $facebook).

Die Namen sind eigentlich selbsterklärend. Die Funktion FriendSelect() erzeugt den Friend-Selektor, den wir uns im vorherigen Teil gebastelt haben. Die Funktion DisplayRanking() erzeugt eine Liste der Top-10-Teilnehmer des Quizes. Hier könnten Sie natürlich noch einiges mehr machen, so könnten Sie nicht nur die Top 10 ausgeben sondern auch dem Nutzer mitteilen, auf welchem Platz er liegt, etwa im Vergleich zu seinen Freunden, die das Quiz ebenfalls beantwortet haben. Die Funktion RetrieveData() ermittelt zunächst die Daten von der Facebook API und hinterlegt diese dann in ihrer Datenbank.

Diese einfache Anwendung zeigt, wie leicht der Einstieg in die Facebook-Anwendungsentwicklung mit PHP sein kann. Viel Spass dabei!

(mm),

Kategorien
Webdesign

Herr der Nutzerdaten – Facebook-Anwendungen mit PHP, Teil 2

Als Anwendungsentwickler für Facebook sind die Nutzerdaten ein großer Anreiz: Damit kann man zum einen natürlich gezielt werben – zum anderen kann man mit den Daten auch Schindluder betreiben. Der verantwortliche Umgang mit den Nutzerdaten ist wichtig, doch leider gibt ein Nutzer, nach dem Hinzufügen einer Anwendung in gewisser Hinsicht die Hoheit über die eigenen Daten auf.

Viel zu wenige Nutzer editieren die Optionen zur Privatssphäre, welche Facebook bereitstellt. Deshalb ist es ein leichtes, über minimalistische Anwendungen an Daten zu gelangen. Dieser Teil der Reihe zur Anwendungsentwicklung für Facebook mit PHP zeigt die Wege auf, mit denen es Facebook einem Anwendungsentwickler erlaubt, Nutzerdaten abzufragen und zum Beispiel in die eigene Datenbank zu überführen.

Die Integrität der eigenen Daten im Netz ist wichtig – gerade auf Facebook sind Nutzer bereit, sehr viel private Informationen an Dritte weiterzugeben. Es scheint, dass vielen Nutzern nicht bewusst ist, auf was für Daten Anwendungen nach einer Authentifizierung durch den Nutzer zurückgreifen können. Grundsätzlich bietet Facebook den Anwendungsentwicklern mehrere Möglichkeiten, an die Nutzerdaten zu gelangen.

Direkter Zugriff über vordefinierte API Funktionen

Die PHP Client Library beinhaltet eine Reihe an Funktionen, mit denen Sie auf einen vordefinierten Satz von Daten zugreifen können. Diese Funktionen stellen zum größten Teil Wrapper dar für einfache SQL-Anfragen an die Facebook-Datenbank. Sie können auch direkt selbst Anfragen als SQL-Selects durchführen.

Dafür gibt es eine Facebook-eigene Query Sprache, die FQL – Facebook Query Language, die eine Teilmenge von SQL ist. Zunächst wollen wir uns Abfragen über die API-Wrapper-Funktionen anschauen.

Die zentrale Funktion ist „users.getInfo“. Über sie kann fast das komplette Profil ausgelesen werden (welches der Nutzer anderen zugänglich macht), sofern die Abfrage mit Verwendung eines Sessionkeys erfolgt. Falls nicht, so können Sie lediglich Information, wie den Namen und das Profilbild abrufen.

<?php
// die Facebook PHP Client-Library einfügen
include_once 'php/facebook.php';
// Ihr API Schlüssel
$api_key = '';
$secret  = '';
// eine Objektinstanz erzeugen
$facebook = new Facebook($api_key, $secret);
//wird verwendet, sodass die Seite den Facebook Look hat - also mit Tabnavigation
$facebook->require_frame();
//Fordert den Nutzer auf, der Applikation den Zugriff zu gewähren
$user = $facebook->require_login();
?>
<p>Hallo <fb:name uid='<?php echo $user?>' capitalize="true" firstnameonly="true" useyou="false" linked="true"/>, dies ist eine minimalistische Facebook Anwendung, die mit der PHP Clientlibrary realisiert wurde - mal schauen, wie weit wir hiermit noch kommen. In der Zwischenzeit kannst du hier doch ein Kommentar hinterlassen...</p>

<fb:comments xid="commentsBox" canpost="true" candelete="false" returnurl="http://apps.facebook.com/cook-off">

<fb:title>Kommentare</fb:title>

</fb:comments>

<p>Oh, siehe da, wir wollen noch ein wenig herumspielen und mal schauen, auf welche Daten wir nun Zugriff haben...</p>

<?php

$userDetails = $facebook->api_client->users_getInfo($user, array ("last_name", "first_name", "name", "pic", "birthday","current_location","hometown_location", "status", "political", "work_history"));

echo "<p>Dein Name ist ".$userDetails[0]['first_name']." und du kommst aus ".$userDetails[0]['hometown_location']['city']." in ".$userDetails[0]['hometown_location']['state'].", zur Zeit lebst du aber in ".$userDetails[0]['current_location']['city'].".</p>";

//mit Hilfe des Variable Dump sehen Sie die Struktur des zurückgelieferten Arrays am Besten

echo "<pre>";

var_dump($userDetails);

echo "</pre>";

?>
Anhand des Variable-Dump können Sie am Besten die Struktur des Arrays erkennen. Im Fall der Fälle, dass ein Nutzer Profilinformationen ausgelassen hat, können Sie etwa mit der Funktion isset() eine Abfrage machen. Eine genaue Auflistung, an was für Profildaten Sie mit Hilfe der Funktion users_getInfo() gelangen, finden Sie in der API- Referenz. Die mögliche Informationsfülle ist erstaunlich – neben den politischen Ansichten, können Sie die Erwerbsgeschichte sowie die Ausbildung eines Nutzers abfragen. Je nach Privatsphäreneinstellungen der Nutzer sind manche Informationen sichtbar und manche nicht.

Insgesamt bietet Ihnen Facebook die Möglichkeit per SELECT auf Informationen aus einer ganzen Reihe von Tabellen zurückzugreifen.

Abfrage per Facebook Query Language

Sie können auch direkt über die Facebook Query Language eine Abfrage an die Datenbanktabelle absetzen – natürlich ebenfalls über eine vordefinierte Funktion, welche die Abfrage absetzt.  Sie erhalten in diesen Abfragen jedoch etwas mehr Flexibilität, da Sie zum Beispiel in einer Abfrage Informationen aus verschiedenen SQL-Tabellen gleichzeitig erhalten können. Dazu gibt es in der API eine eigene Funktion. Diese Möglichkeit haben Sie mit den vordefinierten Wrappern nicht.

Zunächst jedoch dieselbe Abfrage wie oben, diesmal als FQL Query. Der Code dazu ist wie folgt:

<?php
$fql = "SELECT last_name, first_name, name, pic, birthday, current_location, hometown_location, status, political, work_history FROM user WHERE ".$user. " IN (uid)";
$userDetails = $facebook->api_client->fql_query($fql);
echo "<p>Dein Name ist ".$userDetails[0]['first_name']." und du kommst aus ".$userDetails[0]['hometown_location']['city']." in ".$userDetails[0]['hometown_location']['state'].", zur Zeit lebst du aber in ".$userDetails[0]['current_location']['city'].".</p>";
echo "<pre>";
var_dump($userDetails);
echo "</pre>";
?>

Die Syntax ist etwas anders als bei SQL – das wird insbesondere im WHERE-Teil deutlich. Facebook unterstützt für FQL keine Abfragen, der Form SELECT * FROM. Um sich vor Angriffen über SQL Injections zu schützen, wird das FQL – bevor daraus eine eigentliche SQL-Abfrage wird – auf mögliche subversive Versuche überprüft. Die Semantik von FQL ist jedoch dieselbe wie die von SQL.

Um an spezifische Werte zu gelangen, etwa wenn Sie von dem Heimatort die Postleitzahl wissen möchten, so können Sie die SELECTS etwas spezifischer gestalten, indem Sie den Query anpassen: „SELECT last_name, hometown_location.zip FROM user WHERE UID IN (uid)“. Damit müssen Sie keine Information verarbeiten, die redundant ist.

Verschachtelte SELECTs

Im ersten Teil wurde ja bereits angekündigt, dass wir unseren Friend-Selector für das Versenden von Einladungen etwas benutzerfreundlicher machen möchten, indem dieser nur noch Freunde in der Liste anzeigt, welche die Anwendung noch nicht hinzugefügt haben.

Die dazugehörige Auswahl kann mit folgender Zeile bewerkstelligt werden:
SELECT uid FROM user WHERE is_app_user = 0 AND uid IN (SELECT uid2 FROM friend WHERE uid1 = ".$user.")
Hier sieht man auch schön die verschachtelte Natur der Anfragen.

So sieht der fertige Code aus:

 <?php
// die Facebook PHP Client-Library einfügen
include_once 'php/facebook.php';
// Ihr API Schlüssel
$api_key = '';
$secret  = '';
// eine Objektinstanz erzeugen
$facebook = new Facebook($api_key, $secret);
//wird verwendet, sodass die Seite den Facebook Look hat - also mit Tabnavigation
$facebook->require_frame();
//Fordert den Nutzer auf, der Applikation den Zugriff zu gewähren
$user = $facebook->require_login();
if(isset($_GET["action"]) && $_GET["action"] == 'step1')
{
?>
<p>Hallo <fb:name uid='<?php echo $user?>' capitalize="true" firstnameonly="true" useyou="false" linked="true"/>, bevor es richtig los geht -- lade doch deine Freunde ein.</p>
<?php
//die UIDs aller Freunde ermitteln, welche die Anwendung bereits hinzugefügt haben
$fql = "SELECT uid FROM user WHERE is_app_user = 1 AND uid IN (SELECT uid2 FROM friend WHERE uid1 = ".$user.")";
$res = $facebook->api_client->fql_query($fql);
//das Ergebnis der Abfrage ist in einem zweidimensionalen Array der Form $res[$i][0]['uid'], wobei $i Zählvariable ist
$friends = "";
//aus dem Ergebnis Array ein String machen, mit Kommatrennung, da wir das als Attribut für unser FBML Element verwenden können
for($i=0; $i<count($res);$i++) {
if($i==0) {
if(!isset($res[0]['uid'])) {
$friends = "";
}
else {
$friends = $res[0]['uid'];
}
}
else {
$friends = $friends.",".$res[$i]['uid'];
}
}
$invitationContent ="<fb:name uid=\"".$user."\" firstnameonly=\"true\" shownetwork=\"false\"/> hat sich im <a
href=\"http://apps.facebook.com/cook-off/\">Webgeek Quiz</a> versucht und dachte, dass du es auch ausprobieren
solltest!\n"."<fb:req-choice url=\"".$facebook->get_add_url()."\" label=\"Put Webgeek on your profile\"/>";
?>
<fb:request-form action="index.php?action=step2" method="post" type="cook-off" content="<?php echo htmlentities($invitationContent,ENT_COMPAT,'UTF-8'); ?>">
<fb:multi-friend-selector actiontext="Hier ist die Liste deiner Freunde, die sich noch nicht probiert haben!" exclude_ids="<?php echo $friends; ?>" /></fb:request-form>
<?php
}
elseif(isset($_GET["action"]) && $_GET["action"] == 'step2')
{
echo "<h1>Sehr gut</h2>";
}
else {
?>
<p>Hallo <fb:name uid='<?php echo $user?>' capitalize="true" firstnameonly="true" useyou="false" linked="true"/>, nimm hier an unserem Quiz teil und finde heraus, ob du ein Geek bist.</p>
<h2><a href="index.php?action=step1">Quiz starten</a></h2>
<?php
}
?>

Wer sich diese paar Zeilen Code anschaut, wird feststellen, dass wir dem Ziel einer Anwendung schon etwas näher gekommen sind. Durch die Query-String Variable $action können wir den Benutzer etwas durch die Anwendung lenken. Der Ablauf ist noch relativ simpel – zunächst, nachdem der Nutzer die Anwendung hinzugefügt hat, bekommt er, nach Klick auf „Quiz starten“ zunächst die Aufforderung zu Gesicht, die Anwendung doch seinen Freunden weiterzuempfehlen. Inwieweit das benutzerfreundlich ist, sei dahingestellt – jedoch wird deutlich, wie sie den Nutzer an die Hand nehmen und so durch den Ablauf der Anwendung durchführen können.

Das lesen Sie in der Fortsetzung

Im nächsten Teil wollen wir die Anwendung fertigstellen. Unter anderem sollen:

  • über Formulare Fragen beantwortet werden,
  • diese werden dann, mitsamt einiger demographischer Daten über den Anwendungsnutzer in der eigenen Datenbank hinterlegt;
  • zudem wird ein einfaches Ranking anhand der bisher abgelegten Quizergebnisse erzeugt, sodass der Nutzer sieht, wie er relativ gesehen, abgeschnitten hat.

(mm)

Kategorien
Inspiration Tutorials

Facebook Anwendungen mit PHP – Am Anfang steht FBML

In den Beiträgen über die WordPress Facebook Plugins haben wir uns schon ein wenig mit Anwendungsentwicklung für Facebook beschäftigt. Diese Serie soll einen tiefergehenden Einblick in die Entwicklung von Facebook Anwendungen geben – und das mit einem konkreten Beispiel.

Ziel dieses Workshops soll es sein in die wichtigen Elemente der Facebook Anwendungsentwicklung einen Einblick zu gewähren und dabei auf ein konkretes Beispiel zuarbeiten. Zunächst soll die Facebook Markup Language etwas eingeführt werden. Diese wird zur Strukturierung und Darstellung verwendet. Daraufhin soll die API genauer untersucht werden – nämlich, wie und an was für Daten Sie über Facebook kommen. Zudem soll dabei die Facebook Query Language eingeführt werden. Im dritten Teil soll alles zusammenfließen. Unsere Musteranwendung soll ein einfaches Quiz sein, mit einer Reihe von Features. So soll der Nutzer die Anwendung weiterempfehlen können.
Die Inhalte

Um zu starten benötigen Sie einen Zugang als Entwickler von Anwendungen, den es als Facebook Nutzer gratis gibt. Wie in dem Artikel über die Integration eines Blogs in Facebook, müssen Sie eine neue Anwendung erstellen. Sie erhalten wiederum einen Schlüssel und eine Anwendungs ID. Als nächster wichtiger Setupschritt kommt die Definition der Canvas- also der sogenannten „Leinwandseite“. Hier wird es etwas kniffliger. Sie können selbst den URL zu der Leinwandseite bestimmen, diese hat die Form: http://apps.facebook.com/anwendungsname. Die Canvas Callback URL ist die Adresse auf ihrem Webserver, auf dem die Anwendung eigentlich liegt – zum Beispiel http://www.ihreseite.de/anwendungsname. Facebook leitet alle Anfragen der Form http://apps.facebook.com/anwendungsname/anfrage an http://www.ihreseite.de/anwendungsname/anfrage weiter.

Als Nächstes müssen Sie entscheiden, ob sie FBML (Facebook Markup Language) oder ein Iframe wählen. Der Unterschied wurde ebenfalls in dem Artikel über Facebook und WordPress schon einmal angesprochen. Bei FBML handelt es sich um Facebook XML Schema, mit dem Sie auf verschiedene vordefinierte Prozeduren von Facebook zurückgreifen können. Dadurch können Sie zum Beispiel durch einen FBML-Tag das Bild eines Nutzers ihrer Applikation einfügen lassen – genauso gibt es Tags zum Darstellen einer Kommentarbox und viele mehr. Eine ausführliche Diskussion der Vor- und Nachteile der zwei Methoden, finden Sie auf der Dokumentationsseite von Facebook.

Vor- und Nachteile von FBML

Vorteile:

  • einfacher Zugang, schnell zu erlernen – Umfangreiche Referenz
  • Zugriff auf viele vordefinierte Facebook Funktionen und Elemente
  • gibt der Applikation schöne URLs

Nachteile

  • sie können Javascript nicht verwenden, sondern müssen auf Facebook Javascript zurückgreifen, d.h. vorhandenen Javascript Code müssen Sie anpassen
  • kann langsamer sein, da stets die komplette Facebook Seite geladen werden muss (und nicht nur der Inhalt des IFrame)

Vor- und Nachteile von IFrame

Vorteile:

  • Verwendung von AJAX, CSS, Javascript wie gewohnt
  • einfacher, da vorhandene Scripts nicht oder nur kaum angepasst werden müssen
  • auf lange Sicht, einfacher für Anwendungsentwickler, da sie sich nicht an das restriktive FBML-Schema halten müssen
  • bessere Performance

Nachteile:

  • kein Zugriff auf FBML-Tags, sondern nur auf XFBML, was nur eine Teilmenge von FBML darstellt
  • Anfangs etwas komplizierter und langwieriger zu erlernen

Um nun mit ihrer Applikation loszulegen, benötigen Sie noch eine PHP-Bibliothek, die von Facebook gestellt wird. Mit dieser Bibliothek ist der Zugang zur Facebook API sehr leicht und die Bibliothek bietet sehr viele vordefinierte Funktionen und Routinen, mit denen sie schnell in der Anwendungsentwicklung durchstarten können.

Die Dokumentation zur PHP Client Library ist sehr dürftig, jedoch finden Sie in der Datei „facebookapi_php5_restlib.php“ im Unterordner „PHP“ einen Satz von Funktionen, mit dem Sie arbeiten können – aus den Kommentaren kann man auf deren Funktion schließen. Da die Client Library von SimpleXML gebrauch macht, funktioniert die Standardbibliothek nur mit PHP5. Sie können jedoch eine inoffizielle PHP-4-kompatible Version herunterladen.

Nun wollen wir aber loslegen. Ziel soll es zunächst sein, eine einfache „Hello World“-Anwendung zu entwickeln und dabei in die grundlegenden FBML-Tags einzuführen.

<?php
// die Facebook PHP Client-Library einfügen
include_once 'php/facebook.php';
// Ihr API Schlüssel
$api_key = '###########';
$secret  = '###########';
// eine Objektinstanz erzeugen
$facebook = new Facebook($api_key, $secret);
//wird verwendet, sodass die Seite den Facebook Look hat - also mit Tabnavigation
$facebook->require_frame();
//Fordert den Nutzer auf, der Applikation den Zugriff zu gewähren
$user = $facebook->require_login();
?>
<p>Hallo <fb:name uid='<?php echo $user?>' capitalize="true" firstnameonly="true" useyou="false" linked="true"/>, dies ist eine minimalistische Facebook Anwendung, die mit der PHP Clientlibrary realisiert wurde - mal schauen, wie weit wir hiermit noch kommen. In der Zwischenzeit kannst du hier doch ein Kommentar hinterlassen...</p>
<fb:comments xid="commentsBox" canpost="true" candelete="false" returnurl="http://apps.facebook.com/cook-off">
<fb:title>Kommentare</fb:title>
</fb:comments>

Zwischenschritte
Bevor Sie das Ergebnis zu Gesicht bekommen, müssen Sie zunächst die Applikation zu Ihren Anwendungen hinzufügen und der Anwendung Zugriff gewähren. Dann könnte das, was Sie zu Gesicht bekommen, wie folgt aussehen.

So sieht die erste Ausgabe mit ein paar FBML-Tags aus

Sie haben hier die Bekanntschaft mit ein paar FBML-Tags, sowie einigen Funktionen aus der Client Library gemacht. Aufgrund der Funktion require_frame() wird das bekannte Facebook Design geladen, samt der Tabbed Navigationsleiste und des Facebook Stylesheets. Der Aufruf $facebook->require_login() führt dazu, dass ein Nutzer zunächst den Zugriff der Anwendung zulassen muss. Das Objekt, in dem Fall die Variable $user, steckt die User Identification Number (UIN), über welche Sie dann Zugriff auf das Profil und die Information darin haben.

Dies wird über die FBML-Tags verwendet, indem das Bild des Nutzers über <fb:profile-pic uid='<?php echo $user?>‘ linked=“true“/> eingeblendet wird. Eine Kommentarbox wird durch den FBML-Tag <fb:comments> eingefügt.

Die Facebook Markup Language beinhält neben Tags zum darstellen von Facebook Elementen, wie eben der Kommentarbox, auch Elemente, mit der man quasi-prozedurale Strukturen definieren kann. Im folgenden sollen noch einige FBML-Tags vorgestellt werden. Auf den Entwicklerseiten gibt es eine kleine Referenz und mittlerweile gibt es auch schon ein Buch zur Facebook Anwendungsentwicklung mit FBML im O’Reilly Verlag.

Bedingungen und prozedurale FBML-Tags

Mit einfachen Bedingungen können Sie insbesondere steuern, was die Besucher ihrer Applikation zu Gesicht bekommen und was nicht, abhängig zum Beispiel von dem Login Status oder den Einstellungen zur Privatsphäre.

<fb:if-can-see uid="12345" what="profile">
Sie dürfen das Profil von dem Nutzer mit der Kennung 12345 betrachten.
<fb:else>
Sie nicht.
</fb:else>
</fb:if-can-see>

Ein weiterer FBML-Tag dazu ist zum Beispiel:

<fb:if-is-app-user> Sie haben die Anwendung installiert!
<fb:else> Sie noch nicht. </fb:else>
</fb:if-is-app-user>

Formulare

Das werden wir später noch ausgiebig gebrauchen. Formulare können direkt über FBML erzeugt werden, jedoch gibt es dort auch Grenzen des Möglichen. Unser Ziel soll sein, eine einfache Quizanwendung auf Facebook zu erzeugen. Von diesen gab es eine ganze Schwemme vor einigen Monaten – mittlerweile gibt es bereits Anwendungen, mit denen Sie ihr eigenes Quiz erzeugen können. Wir wollen uns aber die Hände selber schmutzig machen.

Folgender Code wird verwendet um eine Box einzufügen, mit der Sie Freunde und Bekannte einladen können, ihre Anwendung hinzuzufügen.

<fb:request-form action="index.php"  method="POST" invite="true" type="Quiz" content="Wollten Sie schon immer mal ihr Webwissen testen? <?php echo htmlentities("<fb:req-choice url=\"http://apps.facebook.com/ihre-application\" label=\"Einladung senden.\"") ?>" >
<fb:multi-friend-selector showborder="false" actiontext="Laden Sie Ihre Freunde ein und Messen Sie ihr Wissen!">
</fb:request-form>
FBML Formular mit dem Sie Freunde einladen können, ihre Anwendung hinzuzufügen

Im folgenden bietet sich zum Beispiel so ein Formular irgendwo dazwischen zu schalten. D.h. wenn wir eine Quizanwendung entwickeln, könnten wir versuchen so ein Formular einzubinden, bevor der Nutzer sein Ergebnis aus dem Quiz zu Gesicht bekommt. Das wird bei sehr vielen Anwendungen so gehandhabt. Später wollen wir das obige etwas anpassen, sodass nur Freunde auswählbar sind, welche die Anwendung noch nicht hinzugefügt haben.

Anwendungsnutzer anzeigen

Mit diesem FBML-Tag können Sie das Profilbild der Nutzer anzeigen, welche ihre Anwendung bereits hinzugefügt haben. Später soll daraus soetwas wie eine Bestenliste werden. Mehr dazu aber später.

<fb:user-table>
<fb:user-item uid="683545112"/>
<fb:user-item uid="4"/>
<fb:user-item uid="7403766"/>
</fb:user-table>

Einzelne Profilbilder

Mit Hilfe der UID können Sie Profilbilder von einzelnen Nutzern anzeigen – das Profilbild von Mark Zuckerberg kann mit folgendem FBML-Tag eingefügt werden:

<fb:profile-pic uid="4" size="square"/>

Weiter oben wurden die Vor- und Nachteile von FBML beziehungsweise IFrames für die Canvas-Seite vorgestellt. Falls sie FBML gewählt haben und dennoch einmal ausgiebig Javascript nutzen möchten, können Sie ein IFrame einfügen. So können Sie in gewisser Hinsicht von Iframes indirekt dennoch Gebrauch machen – jedoch müssen Sie im Iframe komplett auf FBML verzichten. An das IFrame wird standardmäßig Information wie die Signatur des Nutzers sowie ein Sessionkey über das src-Attribut als Query-String an den URL hinzugefügt, sodass Sie den Nutzer auch über den IFrame noch identifizieren können.

Share Button

Vielleicht haben Sie sich schon einmal gefragt, was für Informationen bei dem Share-Button standardmäßig geladen werden. Im Endeffekt können Sie da selbst bestimmen — möchten Sie eine Webseite ihren Freunden mitteilen, so werden in der Regel die Metatag Beschreibung ausgelesen. Mit folgendem FBML Code können Sie selbst bestimmen, was als Text und was für ein Icon geladen werden soll.

<fb:share-button class="meta">
<meta name="medium" content="mult"/>
<meta name="title" content= "Titel">
<meta name="description" content="Beschreibung"/>
<link rel="image_src" href=http://www.test.de/images/icon-big.gif />
<link rel="target_url" href=http://www.test.de/>
</fb:share-button>

Im nächsten Teil wollen wir unsere Anwendung ein wenig strukturieren – zunächst aber soll die Frage beantwortet werden, wie wir Daten über unsere Anwendungsnutzer abfragen und zum Beispiel in einer eigenen Datenbank ablegen können. Daraufhin soll unsere Quiz Anwendung vorgestellt werden.

(mm),

Kategorien
Webdesign

Was ist… Lexikon: SOAP

SOAP, das ist hier nicht Seife, sondern steht für „Simple Object Access Protocol“ und findet sich als solches hinter fast allen Webservices. Es ist ein Protokoll, über welches Daten zwischen Systemen oder Applikationen ausgetauscht werden kann. SOAP selbst basiert auf einem XML-Schema zur Darstellung von Daten sowie einem Protokoll, über welches der Transport von Daten abgewickelt wird. In der Regel wird hierfür TCP oder HTTP verwendet. Es ist aber auch möglich, FTP, SMTP oder JMS als Übertragungsprotokoll zu verwenden. In der Praxis wird jedoch auf HTTP(s) zurückgegriffen.

Als Vorgänger von SOAP kann man XML-RPCs bezeichen – das steht für „Extensible Markup Language Remote Procedure Calls“, welches ebenfalls den Aufruf von Funktionen über verschiedene Systeme ermöglicht.  XML-RPC verwendet für den Transport HTTP und für die Darstellung XML. Das heißt, es ist deutlich weniger flexibel als SOAP, jedoch auch schlanker und einfacher zu erlernen.

Viele Webservices funktionieren sowohl über XML-RPCs als auch über SOAP, zum Beispiel die Amazon Webservices. Mit ihrer Hilfe kann ein Nutzer einen eigenen Amazon Webshop aufbauen, da er dank Webservices über SOAP kompletten Zugriff auf die Produktdatenbank von Amazon hat. Es können so zum Beispiel Daten aus einer SQL Datenbank für dritte zugänglich gemacht werden, ohne das diese Nutzer sich auf ihrem Server befinden müssen. Man kann Zugriff auf die eigene Datenbank gewähren und diesen steuern, indem man  gewisse Abfragen nicht zulässt.

SOAP hat bisher den Status einer W3C Empfehlung. Das XML-Schema definiert eine Syntax, nach welcher der Datenaustausch zwischen Webdiensten oder Applikationen innerhalb eines Netzwerkes auszurichten ist. Dabei werden jedoch keine Einschränkungen auf die Natur und Art der Daten, die ausgetauscht werden, gemacht. So ermöglicht SOAP eine sehr flexible Anwendungsentwicklung. Es wird insbesondere dann eingesetzt, wenn zwischen Softwarearchitekturen Kompatibilitätsprobleme herrschen – hier können über SOAP Brücken zwischen Systemen geschlagen werden.

Schema einer SOAP Nachricht

Das Schema einer SOAP-Nachricht ist stets dasselbe – es besteht aus einem Header, sowie einem Körper. Diese Elemente müssen auftauchen — in der Art, welche Daten verschickt werden und welches Format diese haben, hat der Entwickler jedoch volle Flexibilität.

Die Flexibilität der Kommunikation mit SOAP hat jedoch auch Konsequenzen, da Informationen stets in ein XML-Dokument gepackt werden müssen, welches valide ist. Dies wird stets vor dem Absenden überprüft. Durch das XML-Schema wird der Datenaufwand für das Versenden selbst von kleinen Informationspaketen multipliziert. PHP-Webentwickler haben eine Reihe von Programmbibliotheken zur Verfügung, mit denen Sie mit SOAP arbeiten können:

  • PEAR::SOAP – dieses Paket ist im PEAR-Installer standardmäßig ab PHP 4.3.x enthalten
  • nuSoap – einfach zu benutzende SOAP-Implementierung

Eine Hauptaufgabe der Bibliotheken ist die Interpretation der SOAP Antworten sowie die Erzeugung und das Absetzen der Anfragen – dazu benötigen die Bibliotheken einen XML-Parser, zum Beispiel den Expat XML Parser. ™

Kategorien
Webdesign

OpenID demystified – Teil 2 mit Google Account

Im ersten Teil haben wir die Authentifizierung über OpenID mit der PHPOpenID Klasse durchgespielt. Dabei konnten wir Nutzerdaten über die OpenID Erweiterung SReg abfragen. Diese wird jedoch nicht von allen OpenID Anbietern unterstützt. In diesem zweiten Teil wollen wir uns die Erweiterung „AX“ für OpenID genauer anschauen und probieren, ob ein Login auch über eine Google OpenID funktioniert.

Was ist meine OpenID von Google?

Wenn Sie GoogleMail Nutzer oder irgendeinen Google Dienst nutzen, so haben Sie einen Google Account und damit auch eine OpenID. Wenn Sie ein Blog auf Blogger.com oder Blogspot.com besitzen, so können Sie als ihre OpenID den URL zu ihrem Blog benutzen. In meinem Fall ist das http://fetzert.blogspot.com/. Wenn Sie ihre Blogger URL als OpenID in unser Formular oben eingeben, werden Sie automatisch weitergeleitet auf die Seite von Blogger und müssen sich dort einloggen. Alles funktioniert wie gehabt – doch was, wenn Sie keine OpenID in der Form eines URL haben? Selbst dann haben Sie eine OpenID von Google. Nur, dass diese etwas anders aussieht – beziehungsweise nicht mehr die Form eines URL hat. Google setzt hier auf die vorher erwähnten XRI.

Damit unser Login auch mit Google funktioniert müssen Sie als URL für Ihre OpenID die Adresse http://www.google.com/accounts/o8/id in das Login-Feld eintippen. Sie werden dann automatisch auf die Login-Seite von Google weitergeleitet und können den Zugriff der Applikation zulassen.

OpenID Authentifizierung über einen Google Account

Wenn Sie dann auf die OpenIdReturn.php weitergeleitet werden, werden Sie feststellen das Sie keine Information von Google mitgeliefert bekommen haben? Also das Array in welchem eigentlich die Nutzerdaten stecken sollten, ist leer geblieben. Warum denn das?

Es lohnt sich hier, ein Blick hinter die Kulissen zu wagen. Dazu können Sie im Browser einfach einmal die http://www.google.com/accounts/o8/id aufrufen. In der Regel werden Sie nun gefragt ob Sie eine Datei herunterladen möchten oder es wird ihnen folgender XML Code angezeigt (dieser steckt auch in der Datei, welche Sie herunterladen würden).

<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/server</Type>
<Type>http://openid.net/srv/ax/1.0</Type>
<Type>http://specs.openid.net/extensions/ui/1.0/mode/popup</Type>
<Type>http://specs.openid.net/extensions/ui/1.0/icon</Type>
<Type>http://specs.openid.net/extensions/pape/1.0</Type>
<URI>https://www.google.com/accounts/o8/ud</URI>
</Service>
</XRD>
</xrds:XRDS>

Hierbei handelt es sich um ein XML Dokument auf basis des XRDS Schemas. Was ist denn das schon wieder? Ganz vereinfacht ausgedrückt, handelt es sich dabei um ein Dokument, in welchem Metadaten über eine bestimmte Quelle stecken. In unserem Fall wird es verwendet um die Dienste, welche mit einer Resource verbunden sind, in dem Discovery Prozess aufzufinden. Hier findet sich also in dem XRDS Dokument die Adressen für den OpenID Server von Google welcher unter https://www.google.com/accounts/o8/ud liegt. Zudem sagt uns das XRDS Dokment, welche Dienste OpenID von Google unterstützt – eine erste Beobachtung ist, das OpenID 1.1 nicht unterstützt wird. Zudem steht dort nirgendwo etwas von der Erweiterung SReg – das erklärt, warum unser Array leergeblieben ist. Jedoch unterstützt Google AX, also den Attribute Exchange in der Version 1.0. Das wollen wir uns als nächstes etwas genauer anschauen.

Attribute Exchange über OpenID

Nun wollen wir einmal versuchen, dieselben Daten von Google zu erhalten, wie wir sie von myOpenID über SReg erhalten haben. Google verwendet für den Informationstransfer ausschließlich das Attribute Exchange Schema. Dazu müssen wir unser Script etwas anpassen, indem wir komplett auf AX zurückgreifen.

openIdAuthentification/index.php

<?php
if (!isset($_POST['submit'])) {
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>Mit OpenID anmelden</title>
</head>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Personalisieren Sie diese Webseite, indem Sie sich mit Ihrer OpenID <img src="images/openid.gif" alt="OpenID Icon"> anmelden.<br/>
<input type="text" name="id" size="30" />
<br />
<input type="submit" name="submit" value="Einloggen" />
</form>
<?php
} else {
// schauen, ob überhaupt etwas eigegeben wurde
if (trim($_POST['id'] == '')) {
echo "<h1>Geben Sie eine OpenID ein!</h1>";
}
// die Bibliothek einfügen
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/AX.php";
// Sessions erzeugen, mit dieser Session wird später der Nutzer erkannt
session_start();
// Sofern nicht vorhanden, wird ein Flatfilespeicherort für OpenID Authentifizierungen erzeugt
$store = new Auth_OpenID_FileStore('./OpenIdStorage');
// einen neuen OpenID Konsumenten erzeugen
$consumer = new Auth_OpenID_Consumer($store);
// Beginn des Anmeldeprozesses durch Erzeugung einer neuen Anfrage bei dem OpenID Anbieter
$auth = $consumer->begin($_POST['id']);
if (!$auth) {
echo "<h1>Geben Sie eine OpenID ein!</h1>";
}
// AX Anfrage erzeugen - also die Anfrage, über die Nutzerdaten zurückgesendet werden sollen
$axRequestProfileData = array(
'email' => 'http://schema.openid.net/contact/email',
'username' => 'http://schema.openid.net/namePerson/friendly',
'firstname' => 'http://schema.openid.net/namePerson/first',
'lastname' => 'http://schema.openid.net/namePerson/last',
'fullname' => 'http://schema.openid.net/namePerson/'
'country' => 'http://schema.openid.net/contact/country/home',
'dob' => 'http://schema.openid.net/birthDate',
'gender' => 'http://schema.openid.net/gender',
'language' => 'http://schema.openid.net/pref/language',
'web' => 'http://schema.openid.net/contact/web/default',
'about' => 'http://schema.openid.net/media/biography',
'image' => 'http://schema.openid.net/media/image/default',
);
$ax_request = new Auth_OpenID_AX_FetchRequest();
if ($ax_request) {
foreach($axRequestProfileData as $key => $url){
$ax_request->add(Auth_OpenID_AX_AttrInfo::make($url, 1, true, $key));
}
$auth->addExtension($ax_request);
}
else {
echo "<h1> Die Nutzerdaten konnten nicht abgefragt werden</h1>";
}
// Weiterleitung auf den OpenID Anbieter zur Authentifizierung
$url = $auth->redirectURL('http://www.devmag.net/openIdAuthentification', 'http://www.devmag.net/openIdAuthentification/OpenIdReturn.php');
header('Location: ' . $url);
}
?></body>
</html>

Wie sie sehen, hat sich an dem Code selbst nicht sehr viel verändert. Anstatt der „sreg.php“ wird die „ax.php“ geladen. In dem Array $axRequestProfileData wird angegeben, welche Daten Sie auslesen möchten. Die build() Funktion aus der SReg Bibliothek wird hier durch die make() Funktion ersetzt. Diese nimmt als Argumente den URI der Typendefinition und drei optionale Argumente, nämlich die Anzahl – wie oft Sie die Information möchten (Standard ist 1), ob Sie die Information unbedingt voraussetzen (Standard ist false) und den Schlüssel, über welchen Sie später im Array mit den Informationen, die an Sie zurückgekommen sind, auf diese zugreifen.

Es gibt eine Reihe von standardisierten Datentypen. Vielleicht fällt Ihnen auf, dass es zwei verschiedene URIs für die Typendefinitionen gibt, sowohl http://schema.openid.net als auch http://axschema.org. Es ist immer noch nicht abschließend geklärt, wo die AX Schema Definition liegen soll – daher können Sie bisher beide benutzen, wobei Sie jedoch bei manchen Anbietern Probleme bekommen. So unterstützt MyOpenID lediglich die Verweise auf http://schema.openid.net, während Google OpenID sowohl als auch unterstützt. Diese offene Frage ist vor allem für Entwickler problematisch und hat schon zu hitzigen Diskussionen geführt, da noch nicht alle Anbieter auf axschema.org umgestiegen sind.

Um die Daten auszulesen, müssen sie auch die OpenIdReturn.php bearbeiten.

openIdAuthentification/OpenIdReturn.php

<?php
// Wiederum die passenden Includes
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/AX.php";
// die Session wird gestartet
session_start();
// ein Speicherort für die OpenID Daten erzeugen, sofern noch nicht vorhanden
$store = new Auth_OpenID_FileStore('./OpenIdStorage');
// es wird wiederum ein OpenID Consumer erzeugt über welchen die Antwort des OpenID Anbieters
// über den Status der Anfrage ausgelesen wird
$consumer = new Auth_OpenID_Consumer($store);
$response = $consumer->complete('http://www.devmag.net/openIdAuthentification/OpenIdReturn.php');
// im Fall eines erfolgreichen Logins, wird die Session Variable auf true gesetzt.
// durch eine einfache Überprüfung kann nun auf allen Unterseiten überprüft werden, ob
// diese Session Variable den Wert true hat - der Benutzer also eingeloggt ist
if ($response->status == Auth_OpenID_SUCCESS) {
$_SESSION['OPENID_AUTH'] = true;
// die Informationen, die der OpenID Anbieter übermittelt hat, auslesen
$ax = Auth_OpenID_AX_FetchResponse::fromSuccessResponse($response);
echo "<pre>";
var_dump($ax->data);
echo "</pre>";
}
else
{
$_SESSION['OPENID_AUTH'] = false;
}
?>

Probieren wir zunächst die Authentifizierung mit myOpenID aus. Der Prozess läuft zunächst ganz normal ab, sie können wählen, welche Informationen Sie von ihrem Profil an die zu verifizierende Webseite weitergeben möchten. Hier sehen Sie bereits, das dort nichts von einem Profilbild steht, welches jedoch über ‚image‘ => ‚http://schema.openid.net/media/image/default‘ angefordert wurde.

OpenID Authentifizierung auf MyOpenID bei Verwendung von Attribute Exchange

Die Ausgabe der erhaltenen Daten nach erfolgter Authentifizierung bei myOpenID sieht wie folgt aus:

Der Variable Dump der Rückgabewerte von MyOpenID

Insgesamt wird deutlich, dass uns myOpenID nicht wirklich alle Daten übermittelt, die eigentlich angefordert waren. Wie verhält es sich mit Google?

Es stellt sich heraus, das Google noch geiziger ist. Es wird lediglich die Emailadresse des Nutzers herausgerückt. Das ist ein herber Dämpfer in dem Versuch unsere Webseite nach Authentifizierung zu personalisieren. Es sind hauptsächlich Datenschutzgründe, die angeführt werden – die ja zum Teil berechtigt sind. Gerade Google muss vorsichtig bei der Weitergabe von Nutzerdaten an Dritte sein, da Google stets unter besonderer Aufsicht steht – obgleich des Firmenmottos „Don’t be evil“. Es gibt jedoch auch Dienste die bei Google auf einer weißen Liste stehen – an die versendet Google mehr Informationen, etwa plaxo.com. Yahoo! unterstützt weder SReg noch AX – aber eine Implementierung soll irgendwann folgen. Ein Gedanke der dabei sicherlich aufgegriffen wird, ist der Gedanke der multiplen Identitäten, die hinter einer OpenID stecken. So können Sie zum Beispiel bei myOpenID bereits mehrere Identitäten festlegen – für bestimmte Webseiten, können Sie so entscheiden, welche Information sie weitergeben möchten und welche nicht. Man könnte sich zum Beispiel vorstellen, dass man eine Identität mit den grundlegenden Informationen für die Verwendung in Blogs hinterlegt und, vielleicht eine andere, die eher für geschäftliche Zwecke verwendet wird.

Warum ist Attribute Exchange spannend?

Wie bereits gesagt, bietet sich durch Attribute Exchange die Möglichkeit an, ihre Seite oder ihr Blog für Ihre Benutzer zu personalisieren, indem Sie automatisch etwa ein Nutzerbild laden oder den Besucher persönlich ansprechen. In Verbindung mit einer Blogsoftware können Sie so zum Beispiel einem Leser ihres Blogs direkt nach Authentifizierung die neuesten Folgekommentare in Beiträgen zeigen, in denen der Nutzer selbst Kommentare verfasst hat. So wäre das Abonnieren von Kommentaren für bestimmte Beiträge vielleicht obsolet.

Bisher dreht sich OpenID jedoch vor allem um den Nutzen für den Webandwender, also den Besitzer eines Google oder eines Facebook Kontos. Doch bietet OpenID zum Beispiel auch für Institutionen viele Möglichkeiten. Man könnte sich etwa für Universitäten die Verwendung von OpenIDs für die Studenten vorstellen, über welche diese sich für Prüfungen, Seminare oder Kurse anmelden und in Foren an Diskussionen teilnehmen. Man könnte so auf eine OpenID das Emailkonto, sowie das Konto bei der Universitätsbibliothek oder für die Bezahlsysteme in der Mensa zusammenlaufen lassen. Dafür können verschiedene Tiefen der Verknüpfung über multiple Identitäten gewählt werden. OpenID macht dies ohne großen Aufwand möglich, da lediglich die vorhandenen einzelnen Nutzerkonten miteinander verknüpft werden müssen. Genauso bieten sich auch viele Anwendungsmöglichkeiten für Firmen und deren firmeninternen Webpräsenzen an, die ja oftmals auch mit den verschiedensten Diensten aufwarten.

Die Integration über eine OpenID ist systemübergreifend einfach, da lediglich eine Schnittstelle bereitgestellt werden muss, über welche die Nutzer die verschiedenen Dienste miteinander verknüpfen. Was auf dieser Ebene noch geschehen wird ist sehr spannend, bietet sehr viele Potentiale und wird das Internet der Zukunft maßgeblich gestalten.

Kategorien
Webdesign

OpenID demystified – Teil 1

OpenID ist eine Technologie die bereits die kritische Masse an Unterstützung durch große Player wie Google, Yahoo und Facebook erhalten hat. Als Webentwickler darf man sich die Frage stellen, ob und wie man OpenID für die eigenen Zwecke verwenden kann. Es gibt bereits PHP-Klassen, mit deren Hilfe der Zugang zu OpenID relativ leicht ist. Zwar ist OpenID  auf ersten Blick ein leicht zu verstehendes Konzept, dahinter verstecken sich jedoch zahlreiche Prozesse. Wir wollen die Vorgänge bei einer OpenID-Authentifizierung Schritt für Schritt mit einem PHP-Skript nachvollziehen.

Die Struktur einer OpenID-Authentifizierungsanfrage ist bisweilen sehr komplex, da es für die verschiedenen Schritte oftmals mehrere Protokolle oder Schemata gibt, nach denen Anfragen gesendet werden können. Das folgende Bild soll vereinfacht die Struktur einer Anfrage Schritt für Schritt darstellen.

Flussdiagramm weiter unten

Insgesamt gibt es neun Schritte für eine erfolgreiche Authentifizierung. Im ersten Schritt wird von der Applikation als zum Beispiel ihres Webdienstes, was ein Blog oder ähnlich sein kann, eine Anfrage an den Nutzer gesendet. Das hat die Form eines Login-Feldes – so können Sie Ihren Nutzer bitten, sich einzuloggen, um Kommentare in Ihrem Blog abzusetzen. Im zweiten Schritt wählt der Nutzer OpenID als Login und gibt in das Login-Feld seine OpenID ein.

Daraufhin wird der Discovery-Prozess eingeleitet, bei dem zunächst die OpenID überprüft wird. Hier schaltet sich dann auch zum ersten Mal ihr OpenID Provider ein, indem er der Applikation mitteilt, wo der OpenID-Server liegt. Daraufhin fordert die Applikation die Autorisierung durch einen Login des Nutzers an. Jetzt erscheint für den Nutzer das Login-Formular des OpenID-Providers. Der Nutzer gibt seine Login-Daten ein und willigt dem Zugriff ihrer Applikation auf die Daten des Nutzers ein. Im vorletzten Schritt wird die Profilinformation nach erfolgreichem Login an die Applikation weitergeleitet, diese wiederum ermöglicht dem Nutzer im letzten Schritt Zugriff auf die gesicherten Inhalte. In unserem Beispiel darf unser Nutzer jetzt einen Kommentar in dem Blog absetzen.

Für die technische Umsetzung einer Authentifizierung über OpenID mit PHP gibt es einige Bibliotheken:

  • PHPOpenID von JanRain: Diese Bibliothek gibt es seit 2006, seit 2007 in den Versionen 1 und 2. Das OpenID-Protokoll gibt es in den Versionen 1.1 und 2.0 – leider ist die Kompatibilität nicht sichergestellt, da manche Provider aufgrund von Sicherheitsbedenken voll auf OpenID 2.0 ohne Abwärtskompatibilität setzen. Daher lautet die Empfehlung, mit OpenID 2.0 und damit auch mit den Releases aus der PHPOpenID-Version 2 zu arbeiten. Für Applikationen, die noch nicht auf die Releases für Version 2 umgestiegen sind, wird aber die Version 1-Reihe weiter bedient. Mit der Bibliothek kann man sowohl die Authentifizierung durchführen lassen, als auch selber ein Anbieter von OpenIDs werden. Eine Anwendung der Bibliothek stellt der OpenID-Anbieter myopenid.com dar. Der OpenID-Server dort läuft mit der PHPOpenID-Bibliothek.
  • PEAR Package Proposal von Bill Shupp: Es gab bereits mehrere Proposals für ein PEAR Package. Ein neuer Entwurf wurde erst vor wenigen Wochen von Bill Shupp eingereicht. Der bisherige Entwurf sieht aber bisher nur einen OpenID-Konsumenten vor, also eine Bibliothek zur Authentifizierung. Am Bereitstellen eines OpenIDs-Server, welches ebenfalls noch in die Bibliothek integriert werden soll, wird noch gearbeitet. Bill Shupp bietet ein paar Beispiele basierend auf der Bibliothek, mit denen der Authentifizierungsprozess schrittweise im Debug Modus durchlaufen werden kann. Um die Beispiele auszuprobieren, benötigt man eine OpenID, die man etwa von MyOpenID oder Google, Yahoo und Co. erhalten kann. Eine Liste der großen OpenID-Anbieter finden Sie in der Wikipedia.
Das Flussdiagramm stellt die einzelnen Schritte einer OpenID Authentifizierung schematisch dar.

Im Beispiel soll ein Authentifizierungsprozess auf Basis der PHPOpenID-Bibliothek durchgespielt werden. Das ist an und für sich nichts Großes, aber zeigt die Schritte, die für eine Authentifizierung durchgegangen werden müssen. In einem weiteren Schritt soll das Skript erweitert werden, indem es über die OpenID-Erweiterung AX, was für Attribute Exchange steht, zusätzliche Userdaten auslesen kann. Im allereinfachsten Fall werden keine Nutzerinformationen ausgetauscht – es findet lediglich eine Authentifizierung statt. Man kann jedoch relativ unproblematisch auf folgende Daten zugreifen:

  • Nickname
  • Email
  • Voller Name
  • Geburtsdatum im Format YYYY-MM-DD
  • Geschlecht
  • Postleitzahl
  • Land mit ständigem Wohnsitz, die Ländercodes sind nach ISO3166.
  • Sprache, wobei die Sprachcodes nach ISO639
  • Zeitzone

Dieser erweiterte (aber doch noch sehr beschränkte) Informationsaustausch wurde in der Sreg (Simple Registration Extension) für OpenID definiert. Die Attribute Exchange-Erweiterung zu dem OpenID-Protokoll ermöglicht den erweiterten Austausch von Nutzerdaten, hier finden Sie ein Typenschema. Sie beinhaltet auch Prozeduren, mit denen Fallbacks definiert werden können, sollte ein OpenID-Anbieter gewisse Informationen nicht bereitstellen (vielleicht, weil der OpenID Besitzer dies nicht will).

Authentifizierung mit PHPOpenID

Zunächst müssen Sie sich PHPOpenID herunterladen. Im Endeffekt muss lediglich der Ordner „Auth“ in die jeweilige Verzeichnisstruktur eingefügt werden, sodass die Includes auf das richtige Verzeichnis verweisen. Standardmäßig werden die Daten in Flatfiles, also einfachen Textdateien gespeichert. Sie können aber auch MySQL oder PostgreSQL nutzen. Die entsprechenden Bibliotheken werden mitgeliefert.

Alles was Sie für eine Authentifizierung mit einer OpenID benötigen, ist eine OpenID – diese hat in der Regel die Form eines URL. Wenn Sie sich bei MyOpenID.com eine OpenID besorgen, dann hat diese das Format http://ihrname.myopenid.com. Das Login-Formular kann dann wie folgt aussehen:

Das OpenID Login Formular begnügt sicht mit der Eingabe eines URL.

Dabei wurde das nackte HTML-Formular ein wenig mit CSS schöner gemacht. Um den Login durchzuführen, geben Sie ihre OpenID ein. Sofern Ihre ID gültig ist, also das Format eines URL hat und keine ungültigen Zeichen enthält, werden Sie nun zu der Seite ihres OpenID-Anbieters weitergeleitet, um die Authentifizierung durchzuführen. Schauen Sie sich dazu einmal ihre Identitätsseite an, also das, was Sie als Surfer unter ihrer OpenID zu Gesicht bekommen. Das können Sie bei den meisten Betreibern selbst bestimmen. Wichtig sind hier ein paar Zeilen, denn die bestimmen, wohin die Reise geht.

Das heißt, wenn Sie etwa http://ihrname.ihr_open_id_provider.com besuchen und sich den HTML Code anschauen, sollten Sie in etwa sowas zu Gesicht bekommen:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head >
<title>
http://thiemo-fetzer.myopenid.com/
</title>
<link rel="openid.server" href="http://www.myopenid.com/server" />
<link rel="openid2.provider" href="
http://www.myopenid.com/server" />

In meinem Fall ist der OpenID-Anbieter myOpenID, aber viel wichtiger ist, dass Sie hier erfahren, wo die OpenID-Server für die OpenID-Versionen 1 und 2 ihres OpenID-Anbieters liegen. In dem Authentifizierungsprozess ist das einer der ersten Schritte, da die Anfrage nicht an ihre OpenID gesendet wird, sondern an den OpenID-Server. Die Adresse des OpenID-Servers herauszufinden, findet im ersten Schritt des sogenannten Discovery Prozess statt.

Als Nutzer bekommen Sie davon alles nichts mit – sie landen auf einer Seite, die etwa wie folgt aussehen könnte:

Der Login für die Authentifizierung findet auf der Seite des OpenID Anbieters statt, bei dem ein Nutzer seine OpenID registriert hat. Anhand des Designs kann der Benutzer feststellen, ob er an der richtigen Adresse gelandet ist.

Nach der Authentifizierung, die entweder erfolgreich oder eben nicht erfolgreich ablief, werden Sie auf eine von Ihnen definierten URL weitergeleitet. Dazu aber erst später. Das nackte HTML-Formular samt den ersten paar Zeilen Code sind in folgendem Listing:

openIdAuthentification/index.php
<?php
if (!isset($_POST['submit'])) {
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<title>Mit OpenID anmelden</title>
</head>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Personalisieren Sie diese Webseite, indem Sie sich mit Ihrer OpenID <img src="images/openid.gif" alt="OpenID Icon"> anmelden.<br/>
<input type="text" name="id" size="30" />
<br />
<input type="submit" name="submit" value="Einloggen" />
</form>
<?php
} else {
// schauen, ob überhaupt etwas eigegeben wurde
if (trim($_POST['id'] == '')) {
echo "<h1>Geben Sie eine OpenID ein!</h1>";
}
// die Bibliothek einfügen
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
// Sessions erzeugen, mit dieser Session wird später der Nutzer erkannt
session_start();
// Sofern nicht vorhanden, wird ein Flatfilespeicherort für OpenID Authentifizierungen erzeugt
$store = new Auth_OpenID_FileStore('./OpenIdStorage');
// einen neuen OpenID Konsumenten erzeugen
$consumer = new Auth_OpenID_Consumer($store);
// Beginn des Anmeldeprozesses durch Erzeugung einer neuen Anfrage bei dem OpenID Anbieter
$auth = $consumer->begin($_POST['id']);
if (!$auth) {
echo "<h1>Geben Sie eine OpenID ein!</h1>";
}
// Sreg Anfrage erzeugen - also die Anfrage, über die die erweiterten Nutzerdaten zurückgesendet werden sollen
$sreg = Auth_OpenID_SRegRequest::build(array('email', 'fullname', 'dob', 'language'), array('nickname'));
if (!$sreg) {
echo "<h1>Die Nutzerdaten konnten nicht ermittelt werden!</h1>";
}
$auth->addExtension($sreg);
// Weiterleitung auf den OpenID Anbieter zur Authentifizierung
$url = $auth->redirectURL('http://www.devmag.net/openIdAuthentification', 'http://www.devmag.net/openIdAuthentification/OpenIdReturn.php');
header('Location: ' . $url);
}
?></body>
</html>

Dies ist ein Standardbeispiel zur Verwendung der PHPOpenID-Bibliothek. Zunächst wird überprüft, ob bereits über POST eine ID gesendet wurde. Falls dies nicht der Fall ist, wird das Formular angezeigt. Falls doch, so werden zunächst drei Dateien aus der Bibliothek eingefügt. Dabei handelt es sich um die „consumer.php“-Klasse. Diese enthält alle notwendigen Methoden, um eine Authentifizierungsanfrage abzusenden. In der Datei „filestore.php“ befinden sich die notwendigen Funktionen, durch welche die OpenID-Sessions und Daten in Flatfiles abgelegt werden. Es gibt, wie bereits erwähnt, auch Bibliotheken für die Verwendung von Datenbanken als Speichermedium, etwa die „MySQLStore.php“. Die dritte Datei wird benötigt, um Anfragen abzusetzen, über welche die einfachen Nutzerdaten ausgetauscht werden.

Zunächst wird daraufhin ein Speicherort für die Flatfiles definiert. Achtung: Hier müssen Schreibrechte gesetzt werden, da das Skript den Ordner, wenn nicht vorhanden, selbst erzeugt. Im nächsten Schritt wird eine Objektinstanz für den neuen Konsumenten, also den Nutzer, der sich authentifiziert, erzeugt. Diesem wird die Speicheradresse (und somit indirekt auch die Methode der Datenspeicherung) übermittelt.

Der eigentliche Authentifizierungsprozess beginnt im nächsten Schritt, wo aus der „consumer.php“-Klasse die Prozedur begin() aufgerufen wird und an diese die OpenID übergeben wird. Die begin()-Prozedur hat die einfache Aufgabe, die URLs, die als OpenIDs angegeben wurden, zu normalisieren. So wird aus „ihrname.myopenid.com“ etwa „http://ihrname.myopenid.com“. Auch wird auf nicht zugelassene Sonderzeichen und ähnliches überprüft. Über den Aufruf der Prozedur begin() wird der sogenannte „Discovery-Prozess“ eingeleitet. Dabei wird zunächst erkannt, ob es sich bei der OpenID um einen URL handelt oder ein sogenannter XRI Identifier – dabei handelt es sich um eine deutlich kompliziertere Art zur Adressierung von Objekten. Die PHPOpenID Bibliothek unterstützt ab Version 2 sowohl als auch. Darauf soll später noch ein wenig eingegangen werden.

Sollte die Überprüfung erfolgreich verlaufen, so gibt die begin()Prozedur ein Objektinstanz der Klasse Auth_OpenID_AuthRequest zurück. Über diese werden die Anfragen nun versand. Zunächst wird aber noch eine Instanz der Klasse SReg erzeugt, um die Anfrage nach den erweiterten Nutzerdaten mitzusenden.  Diese Anfrage wird über die build()-Funktion erzeugt. Die Anfrage wird daraufhin an unsere Auth_OpenID_AuthRequest-Instanz, welche in $auth liegt, über $auth->addExtension($sreg); hinzugefügt.

Im letzten Teil werden an die Autentifizierungsanfrage noch URLs übergeben. Die erste URL ist die des Dienstes oder der Webseite, welche authentifiziert werden soll. Die zweite URL gibt die Adresse an, auf welche ein Nutzer nach erfolgter Authentifizierung weitergeleitet werden soll.

Die Authentifizierung findet statt - dabei hat der Benutzer die Wahl, welche und ob Informationen an die zu verifizierende Webseite gesendet werden sollen.

Nachdem die Abfrage an den OpenID-Anbieter gesendet wurde, liegt es nun nicht mehr in ihrer Hand. Die Authentifizierung erfolgt auf der Seite des OpenID-Anbieters. Dies stellt wiederum auch ein zusätzliches Sicherheitselement dar, da so der Nutzer anhand des Designs selbst überprüfen kann, ob er hier bei dem richtigen OpenID-Anbieter gelandet ist und nicht etwa bei einem Anbieter, der versucht Daten zu sammeln.

Nach erfolgter Autentifizierung wird der Nutzer nun an die zweite URL weitergeleitet. Jetzt würde uns doch bestimmt heiß interessieren, was für Daten uns der OpenID-Anbieter zurückgesendet hat. Als nächsten Schritt müssen wir uns die Datei OpenIdReturn.php anschauen. Über sie wird die Authentifizierung abgeschlossen, indem die Antwort des OpenID-Anbieters überprüft wird. Im Fall, das die Anmeldung erfolgreich war, können Sie ihren Nutzern Zugriff gewähren.

openIdAuthentification/OpenIdReturn.php

<?php
// Wiederum die passenden Includes
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
// die Session wird gestartet
session_start();
// ein Speicherort für die OpenID Daten erzeugen, sofern noch nicht vorhanden
$store = new Auth_OpenID_FileStore('./OpenIdStorage');
// es wird wiederum ein OpenID Consumer erzeugt über welchen die Antwort des OpenID Anbieters
// über den Status der Anfrage ausgelesen wird
$consumer = new Auth_OpenID_Consumer($store);
$response = $consumer->complete('http://www.devmag.net/openIdAuthentification/OpenIdReturn.php');
// im Fall eines erfolgreichen Logins, wird die Session Variable auf true gesetzt.
// durch eine einfache Überprüfung kann nun auf allen Unterseiten überprüft werden, ob
// diese Session Variable den Wert true hat - der Benutzer also eingeloggt ist
if ($response->status == Auth_OpenID_SUCCESS) {
$_SESSION['OPENID_AUTH'] = true;
// die Informationen, die der OpenID Anbieter übermittelt hat, auslesen
$sreg = new Auth_OpenID_SRegResponse();
$obj = $sreg->fromSuccessResponse($response);
$data = $obj->contents();
echo "<pre>";
var_dump($data);
echo "</pre>";
} else {
$_SESSION['OPENID_AUTH'] = false;
}
?>

Nach erfolgreichem Login können Sie die übermittelten Userdaten anzeigen lassen

Es lohnt sich, den URL einer solchen Anfrage genauer anzuschauen. Über den URL wird schließlich ja dem „Consumer“ mitgeteilt, ob der Login erfolgreich verlaufen ist und irgendwie müssen ja auch die Nutzerdaten an unser Script kommen. Der URL einer erfolgreichen Authentifizierung ist erschreckend lang und soll hier auch nicht reproduziert werden. Wer sich jedoch die Mühe macht, mit URLDecode das Kauderweltsch etwas lesbarer zu machen, der erkennt, dass in dem Query String die gesamte Struktur der Anfrage steckt. Dabei finden Sie auch die Daten, die übermittelt werden, samt der Schlüssel, über welche später im Array auf die Daten zugegriffen werden kann sowie die Adressen der Namespace Definitionen der Protokolle OpenID 2.0 sowie der SReg Erweiterung 1.1.

Nachdem Sie die Authentifizierung durchgeführt haben, werden Sie beim nächsten mal nicht mehr nach ihrem Zugang gefragt, sofern Sie noch bei MyOpenID oder ihrem OpenID-Anbieter eingeloggt sind. Dann ist die Seite bereits in der Liste ihrer „Vertrauenswürdigen Seiten“ und die Authentifizierungsanfrage wird im Schnelldurchgang durchgeführt, ohne dass Sie zum Beispiel angeben müssen, welche Daten übertragen werden sollen und welche nicht.

Im nächsten Schritt können Sie die Datei OpenIdReturn.php modifizieren, indem Sie ihre Nutzer direkt auf eine andere Seite weiterleiten. Sie können nun prüfen, ob die Nutzer eingeloggt sind, indem Sie schauen, ob die Sessionvariable $_SESSION[‚OPENID_AUTH‘] = true; gesetzt ist. Das könnte etwa wie folgt aussehen:

<?php
session_start();
if (!isset($_SESSION
['OPENID_AUTH']) || $_SESSION['OPENID_AUTH'] !== true) {
die ('Der Zugriff auf diese Seite ist
Ihnen nicht gestattet! Bitte melden Sie sich (erneut) an.');
}
?>
<head><title>Kommentar verfassen</title>
</head>

Im zweiten Teil wollen wir versuchen, ob dieser Code auch für eine Authentifizierung mit einer Google OpenID funktioniert – und wie sieht die eigene Google OpenID überhaupt aus? Dieser Teil erscheint morgen. ™

Kategorien
Webdesign

Was ist…Lexikon: API

API steht für „Application programming interface“ was sich auf Deutsch zu einem Wort reduziert – Programmierschnittstelle oder Schnittstelle zur Anwendungsprogrammierung. Über eine Programmierschnittstelle können verschiedene Systeme miteinander interagieren und Informationen austauschen.

Dabei legt eine API zum einen das Format von Anfragen fest, sowie das Vokabular. Oftmals wird für den Informationsaustausch auf das SOAP Protokoll zurückgegriffen, welches ein formelles XML Schema ist, nach dem die Kommunikation zwischen Webservices abläuft. Im weiteren Sinn definiert die API auch die verwendeten Datenstrukturen oder Objektklassen, sowie die Protokolle über welche die Kommunikation durchgeführt wird.

Für Webentwickler werden APIs in ihrer Bedeutung stetig wachsen. Fast alle Branchenriesen bieten bereits eine API an. So können eBay Poweruser auf Desktop Software zurückgreifen, über die Sie ihre Auktionen auf verwalten können. Dies wird ermöglicht durch die eBay API. Nutzer, die für Amazon Werbung in ihren Webseiten schalten, können über deren API auf den Produktkatalog und mittlerweile auch einen gesamten Verkaufsprozess indirekt auf ihrer eigenen Webseite über Amazon abwickeln.

Insbesondere im kommerziellen Bereich sind APIs mittlerweile wichtig. Sie ermöglichen Unternehmen die automatisierte Beschaffung von Zwischen- und Vorprodukten, wobei stets der Lagerbestand mit in Betracht gezogen wird. Sollte der Lagerbestand eine kritisches Level erreichen, werden in automatisierten Beschaffungssystemem automatisch Bestellungen abgesetzt oder Produktionsaufträge in das System des Zulieferers geschrieben. Das spart Zeit und Personalaufwand und führt zu einer zunehmenden Integration der Systeme.

Beispiel einer Anwendung der Google Maps API, zu der es auf Dr.Web auch einen Artikel gibt. Hier sehen Sie auf der Webseite der Londoner Polizei eine Karte, mit den Stadtteilen und den Kriminalitätsraten.

Dies bereitet jedoch auch Sicherheitsrisiken, da über eine API sich ein System nach außen öffnet und so das System angegriffen werden kann, sollten Lücken auftauchen. So können über unsichere APIs zum Beispiel SQL Injections möglich sein. Dabei wird versucht, durch bestimmte Abfragen, welche über die API gesendet werden, die Datenbank zu verändern oder gesicherte Informationen auszulesen.

So könnte eine Abfrage nach den Produktdaten zu dem Produkt mit der ID=56 manipuliert werden, indem die Anfrage modifiziert wird. So kann aus „SELECT * FROM productData WHERE ID=56“ etwa  „SELECT * FROM productData WHERE ID=56 UNION SELECT login, password, ’n‘ FROM userData“. Das „n“ steht hier für die Anzahl der Spalten, da die Anfrage bei einem Union-Select genausoviele Spalten haben muss, wie die ursprüngliche Abfrage. Das ist eine Information, an die man durch die API sehr leicht kommt, weil die API schließlich die Schnittstelle, die Datentypen sowie die Abfragemöglichkeiten definiert. Das heißt durch die Schnittstelle, können sich Systeme weiterer Sicherheitsgefahren öffnen, weil durch sie auch Information über die Systemarchitektur preisgegeben wird.

Auf Dr.Web haben wir uns die APIs von mehreren Webdiensten bereits angeschaut. Eine Liste von APIs finden Sie auf Programmableweb.

Kategorien
Webdesign

Was ist…Lexikon: OpenID

OpenID ist ein so genanntes Single Sign-on-System für Webseiten und andere webbasierte Dienste, welches als Protokoll seit 2005 existiert. Nutzer können sich bei einem OpenID-Provider einmal anmelden und so eine Identität erstellen und sich mit dieser OpenID ohne Benutzername oder Passwort bei allen anderen Systemen und Diensten anmelden, die OpenID angeschlossen sind.

Ein bekannter OpenID-Anbieter ist zum Beispiel Facebook. Als Nutzer legen Sie dort einen Account an und können diesen dann mit anderen OpenID-Diensten wie MySpace oder Yahoo! verknüpfen.

Das hat den Vorteil, dass man lediglich einen Button klicken muss, um Zugriff auf einen Dienst zu erhalten – das heißt Nutzer müssen sich nicht eine Menge verschiedener Accounts und deren Zugangsdaten merken. Das ist eine deutliche Erleichterung für die Nutzer. Eine Liste der OpenID-Anbieter finden Sie in der Wikipedia.

OpenID ist dezentral und basiert auf der Idee, dass eine Identität über einen URL – bei Facebook sind die URLs zum Beispiel „http://www.facebook.com/Ihr_Name“.  Andere Dienste verwenden Subdomains. Da unter Facebook viele Nutzer gleiche Namen besitzen, verwendet Facebook mittlerweile einen Mix aus Subdomain, in welcher in der Regel die Länderkennung steckt sowie einem Pfad mit Ihrem Namen. Beim Verknüpfen ihrer Identität mit einem neuen Service, wird auf diese Identität zurückgegriffen.

Eine OpenID-Identität wird angelegt, sobald Sie sich ein Benutzerkonto bei einem Dienst etwa wie Facebook oder MySpace anlegen. Es steht ihnen jedoch frei, diese Identität auch mit anderen Diensten zu verknüpfen. Wenn Sie ihr Konto mit einem neuen Dienst verknüpfen wollen, wird die Authentifizierung – also der Login über den ursprünglichen OpenID-Anbieter, bei dem Sie ihre Identität angelegt haben, abgewickelt.

OpenID hat Unterstützung von Branchenriesen wie Google, IBM und Microsoft erfahren. Bisher war es jedoch nicht möglich, etwa eine MySpace OpenID-Identität für einen neuen Facebook-Account zu nutzen – wohl weil MySpace und Facebook direkte Wettbewerber sind. Im Frühjahr 2009 kündigte Facebook die vollständige Implementierung von OpenID an.

Es gibt zahlreiche Sicherheitsbedenken die mit OpenID verbunden sind.  So sollte man sich die Frage stellen, was mit einer Identität passiert, wenn der Provider den Dienst einstellt. Auch könnte man die Gefahr wähnen, dass Aufsichtsbehörden oder kommerzielle Anbieter Nutzerverhalten leichter überwachen können. Die Gefahr von Phishing-Attacken ist offensichtlich, da diese umso lukrativer je mehr Accounts mit einer OpenID verknüpft sind. Marco Slot zeigt in einem kleinen Tutorial wie einfach das Phishing mit ein wenig PHP ist. Andererseits können Nutzer leichter die Authentizität von OpenID Diensten ermitteln, da der Login und die Authentifizierung jedes neuen Dienstes über den Login des ursprünglichen OpenID-Providers, bei welchem die Identität angelegt wurde, erfolgt. Mehr Sicherheit sollen zudem die Verwendung von Cookies und Captchas, sowie die clientseitige Verwendung von TLS-Zertifikaten zur Athentifizierung bringen.

Insgesamt bietet OpenID Chancen, um das Internet vor allem benutzerfreundlicher zu machen. Es erlaubt die Verschmelzung von Diensten und das einfachere Verwalten von Benutzerkonten, da so eigentlich nur noch eine Identität verwaltet werden muss. Leider, wie jede Technologie, gibt es Bedenken und Risiken. ™

Kategorien
WordPress

Facebook in ein WordPress Blog integrieren

Die API von Facebook mehr – viel mehr. Im ersten Artikel haben wir ein WordPress-Plugin vorgestellt, mit dem man quasi ein Blogzwilling als Facebook-Applikation erzeugen und in das eigene Facebook Profil einbinden kann. Hier soll ein weiteres Plugin vorgestellt werden, welches „Facebook Connect“ und „OpenID“ verwendet. Damit können Sie regelrechtes Community-Building mit Facebook um ihr Blog herum betreiben.

Zunächst ein wenig Geschichte. Vielen Nutzern ist es ein Graus, für jedes Blog ein eigenen Account zu eröffnen, um Kommentare zu posten. Da bietet es sich an, die Accounts zum Beispiel von sozialen Netzwerken mehrfach zu verwenden. Ein formales Protokoll dafür gibt es seit 2005 mit OpenID. Dabei ist ein Nutzerprofil quasi dezentral für verschiedene Provider von Diensten abrufbar und verwendbar. Das heißt, man kann sich über Facebook ein Account erstellen und diesen Account dann mit einem Blog verknüpfen oder etwa mit einem MSN oder MySpace Account verbinden, ohne das neue Accounts angelegt werden müssen. Das birgt natürlich Sicherheitsrisiken, jedoch auch viele Chancen und Möglichkeiten das Netz benutzerfreundlicher zu machen.

Facebook hat 2007 angekündigt, OpenID vollständig zu implementieren – alle anderen Branchenriesen unterstützen das Protokoll ebenfalls. Und das erklärt nun im Schluss, warum man immer häufiger Buttons, wie „Connect with Facebook“ oder „Google Friend Connect“ begegnet. Das bringt uns wiederum zu unserem Facebook Connect WordPress Plugin, welches Facebook Connect fast nahtlos in ihr WordPress Blog integriert.

Wie bereits erwähnt kann man das Blog mit dem Facebook Zugang nutzen – ist man bereits auf Facebook eingeloggt, muss man sich nicht erneut einloggen. Das ist auch eine Möglichkeit, die mit OpenID geschaffen wurde.

Zudem muss sich der Nutzer nicht für das Blog registrieren, da das Plugin über die Facebook-API die Nutzerinformationen aus Facebook holt. Das ist gerade die Erleichterung für Ihre Nutzer, da sie ihre Daten nicht noch einmal an einer anderen Stelle hinterlegen müssen. Das heißt unter anderem auch, das Profilbild des Nutzers von Facebook ist so sichtbar, was ein Blog doch sehr viel persönlicher macht. Möchte ein Nutzer sein Facebook-Account nicht mit dem Blog verknüpfen, kann er auch wie gewohnt einen eigenständigen Account anlegen.

Das Plugin versucht, viele Facebook Funktionaliäten zu integrieren. So können Sie Einladungen an Freunde in ihrem Netzwerk senden – ihr Blog kann so vom „Word-of-Mouth“ Marketing profitieren. Dies kann dadurch noch ausgeweitet werden, wenn Sie im Plugin die Option akivieren, welche die Aktivität ihrer Nuzer auf ihrem Blog, zum Beispiel die Kommentaraktivität in den Feed auf Facebook integriert. Dieser ist dann wiederum sichtbar für Freunde und Bekannte, wodurch Sie eventuell weitere Leser gewinnen können. Zu guter Letzt – Sie können, wie bei „Google Friend Connect“ ein Gadget einbinden, welches die letzten Besucher auf ihrem Blog samt Profilbild anzeigt.

Facebook Connect Implementation vom Plugin-Programmierer Sociable.es
Facebook Connect Implementation vom Plugin-Programmierer sociable.es (auf Spanisch)

Im Endeffekt wird mit diesem Plugin der Spieß umgedreht. Im vorangehenden Artikel stellten wir ein Plugin vor, mit dem man das Blog und die Funktionalität in Facebook integriert – hier soll dem Blog die Möglichkeit gegeben werden, Inhalte und Funktionalitäten aus den sozialen Netzwerken zu verwenden, ohne in diese integriert zu werden.

Plugin Installation und Konfiguration

Zunächst muss, wie im vorherigen Artikel beschrieben, ein Zugang zur Facebook-API eingerichtet werden. Für diesen erhalten Sie dann einen API-Schlüssel. Für das Plugin müssen sie wiederum eine eigene Anwendung erstellen, für welche Sie wiederum eine Anwendungs-ID erhalten.

Neue Facebook Applikation erstellen
Neue Facebook Applikation erstellen

Sie müssen zudem die Callback-URLs definieren. Diese verweisen auf das Root-Verzeichnis Ihres Blogs.

Callback URLs für die neue Facebook Applikation definieren
Callback URLs für die neue Facebook Applikation definieren

Danach ist die Arbeit eigentlich schon erledigt und der Spaß kann beginnen. Sie müssen nun das Plugin von der Webseite des Programmierers herunterladen. Laden Sie dieses in das Plugin-Verzeichnis ihrer WordPress-Installation und entpacken Sie es dort. Das Plugin selbst besteht zum großen Teil aus der Programmbibliothek „Facebook Connect“, die von Facebook stammt. Eine Datei könnte Ihnen dabei auffallen, da sowohl eine PHP als auch eine HTML-Datei mit gleichem Namen auftaucht – die Datei „xd_receiver.htm“. Diese hat eine zentrale Rolle, da über Sie die „cross-domain communication“ abgewickelt wird. Daher auch der Name x für cross und d steht für Domain.

Was hat es damit auf sich? Typischerweise werden HTTP-Anfragen über das XMLHttpRequest behandelt. Die Sicherheitseinstellungen im Browser erlauben jedoch nur die Nutzung von XMLHttpRequest, sofern die Anfragen an dieselbe Domain geschickt werden. In unserem Fall müssen Anfragen jedoch von und an Facebook gesendet werden können. Das wird mit so genannter „Iframe Cross Domain Communiation“ erledigt. Dabei erzeugt die Applikation auf facebook.com ein Iframe mit der entsprechenden Anfrage, etwa ob ein Nutzer bereits bei Facebook eingeloggt ist. Diese Anfragen werden über den URL des Iframes erzeugt. Es wird dann wiederum auf der Domain von facebook.com, die Anfrage überprüft – also etwa der Login Status. Daraufhin wird auf der Applikationsdomain ein Iframe erzeugt, an welches das Ergebnis gesendet wird. Das Ergebnis der Anfrage landet als Query-String an die Datei xd_receiver.htm auf dem eigenen Server. Somit wurde die Nutzung von XMLHttpRequest umgangen.

Nun zurück zu unserem Plugin. Nachdem dieses in das Plugin-Verzeichnis hochgeladen wurde, kann es aktiviert werden. Daraufhin können Sie die Einstellungen des Plugins verändern und es als Widget zum Beispiel in Ihre Sidebar einfügen. Zunächst sind jedoch API-Key und Schlüssel anzugeben.

Einstellungen für das Facebook Connect Plugin
Einstellungen für das Facebook Connect Plugin

Wie sie sehen, können Sie hier bereits gewisse Optionen aktivieren – so zum Beispiel das automatische Einfügen von Kommentaren, ohne das diese vorher moderiert werden. Da sich auf Facebook in der Regel nur natürliche Personen tummeln, ist das eine Option, die man vielleicht aktivieren kann.

Der „Facebook share“-Button kann ebenfalls automatisch an ihre Blog-Posts angefügt werden – zudem können Sie die Option aktivieren, das Kommentare ihrer Nutzer in deren Aktivitätsfeed auf Facebook integriert werden.

Nachdem Sie diese Einstellungen angepasst haben, werden Sie informiert, dass Sie für die Darstellung kein Template definiert haben. Um ein Template zu aktivieren und mit Facebook zu „synchronisieren“, müssen sie nun bei den Einstellungen etwas weiter nach unten scrollen und die Templates erzeugen und dann aktivieren.

Facebook Connect Template Einstellungen anpassen
Facebook Connect Template Einstellungen anpassen

Wie Sie sehen, können Sie hier auch die Sprache über die Templates anpassen. Das erste Template ist zum Beispiel für die Aktivitätsfeeds auf Facebook. Diese können Sie jedoch auch in ihr Facebook Connect Gadget integrieren (siehe das Beispiel auf sociable.es).

Nun bleibt eigentlich nur noch, dass „Facebook Connect Gadget“ als Widget in die Sidebar einzufügen. Dazu müssen Sie nun auf die Seite für die Widgets wechseln und das „Facebook Connect Widget“ in Ihre Sidebar kopieren.

Facebook Connect Widget in die Sidebar integrieren
Facebook Connect Widget in die Sidebar integrieren

Auch hier haben Sie einige Konfigurationsmöglichkeiten – insbesondere können Sie die Sprache anpassen und entscheiden ob ein großer „Connect to Facebook“ Button angezeigt werden soll. Nachdem das Widget installiert ist, sollten Sie erst einmal herumprobieren, was das Plugin für Sie bereit stellt. Insbesondere das Widget auf sociable.es stellt mit den Tabs eine schöne Implementation dar. ™

Kategorien
WordPress

Ein WordPress Blog in Facebook integrieren

Seit Mai 2007 gibt es die Facebook-API und ein Netzwerk aus Entwicklern hat diese schon eifrig auf Herz und Nieren getestet, so dass tagtäglich neue Applikationen auf Basis der API entstehen. Das „Gesichtbuch“ sprengt die Superlative – mehr als 250 Millionen Benutzer verbringen jeden Tag insgesamt mehr als 5 Milliarden Minuten in dem Netzwerk. Zeit, sich Gedanken zu machen, wie man Facebook etwa für das Blogging verwenden kann.

In Deutschland ist Facebook noch ein relativ „kleiner“ Player mit großem Potenzial. Im Juli gab es etwa 3,4 Millionen aktive Nutzer, Tendenz steigend, hauptsächlich in der Altersgruppe von 18-35 Jahren. Im Vergleich: bei Studivz sind etwa 14 Millionen Nutzer aktiv dabei. Facebook kann also in Deutschland auch prächtig wachsen, trotz der Konkurrenz durch Studivz.

Die ersten Applikationen auf Basis der Facebook-API waren direkt in das Netzwerk integriert. So gibt es zum Beispiel jede Menge interaktive Browser-Spiele, welche man über das Netzwerk mit seinen Freunden spielen kann. Aktuell ist zum Beispiel die Applikation „Mafia Wars“ ein Renner. Dabei muss man als Spieler eine „Mafia Familie“ mit seinen Freunden gründen und um Geld zu verdienen, Aufgaben wie Schutzgeld einsammeln und so weiter erledigen. Die Applikationen wachsen dabei durch ein einfaches Schneeballsystem – denn, um zum Beispiel eine „Mafia Familie“ zu gründen, muss man Freunde dazu bewegen, dieser beizutreten – diese stehen dann wieder vor der Wahl, Freunde einzuladen, um eine eigene Familie zu gründen. So multipliziert sich die Nutzeranzahl und die Applikationen wachsen bisweilen rapide.

Diese Applikationen tragen sich in der Regel durch Vermarktung und Werbung und machen, so ganz nebenbei, Facebook noch attraktiver. Das kann auch eine Erklärung für das rasante Wachstum von Facebook auch in Deutschland sein.

Seit Anfang 2009 gibt es zum Beispiel das Plugin Wordbook für WordPress von John Eckman. Mit diesem Plugin können WordPress Nutzer ihre Posts auch direkt ins Facebook stellen und den Nutzern das Kommentieren, sowie das Weiterverteilen über das bekannte Facebook-Interface ermöglichen. Damit lebt das Blog im Endeffekt zweimal – einmal auf Facebook als Applikation (zum Beispiel: mein privates Blog) http://apps.facebook.com/freigeistblog/ und einmal unter der ursprünglichen URL http://freigeist.devmag.net/.

Möchte man über Facebook auf das Blog zugreifen, muss man zunächst der Applikation den Zugriff erlauben. Dazu muss man lediglich die URL der so genannten Leinwandseite aufrufen (Beispiel). Damit hat der Betreiber des Blogs von dem Zeitpunkt an Zugriff auf Daten von Nutzern der Applikation (auf was die meisten Applikationen auch ganz heiß sind – denn mit Informationen wie Geburtsdatum, Geschlecht und Wohnort lässt sich die Werbung doch sehr zielgesteuert einblenden).

Bei unserer Applikation handelt es sich lediglich um ein Blog, das heißt, es ist sehr unschuldig. Bisher werden in der Facebook-API Blogs oder Webseiten als Applikationen behandelt, vielleicht wird sich das aber in Zukunft ändern. Facebook-Nutzer, welche das Blog als Applikation hinzugefügt haben, können nun direkt über Facebook Kommentare senden. Diese werden im Blog angezeigt, obgleich man über Facebook oder das Blog direkt besucht. Über die „Share This“ Funktion kann man anderen Facebook-Freunden Posts weiterleiten.

Diese Funktionalität ist an und für sich noch recht beschränkt und Communities kann man um das Blog so nur schwer bilden. Doch das Potenzial scheint sehr groß, denn man kann die Aktivität in einem Blog so direkt in den eigenen Facebook-Newsfeed einbinden – also der Feed, der beim einloggen, die letzten Aktivitäten von Freunden anzeigt. So können zum Beispiel Kommentaraktivität in diese Feeds eingebunden werden, welches dann auch den Freunden im Feed sichtbar wird. So kann, quasi schneeballmäßig „Word-to-mouth“-Marketing betrieben werden, mit denen Sie als Blog-Betreiber neue Leser aus dem Freundeskreis anwerben können.

Um das eigene WordPress-Blog mit dem Plugin-WordBook mit Facebook zu verknüpfen, sind nur wenige Schritte nötig. Zunächst müssen Sie auf hier eine neue Applikation erzeugen. Dazu benötigen Sie einen Facebook-Account und müssen sich unter der Adresse als Entwickler registrieren. Das alles ist ein Aufwand von wenigen Minuten.

Eine Facebook Applikation erzeugen
Eine Facebook Applikation erzeugen

Nachdem Sie den Nutzungsbedingungen zugestimmt haben und der Applikation einen Namen gegeben haben, sehen Sie direkt ihren API-Schlüssel.

Einstellungen für eine Facebook Applikation
Einstellungen für eine Facebook Applikation

Der nächste Schritt ist es, eine Post-Authorize Callback-URL zu definieren. Dabei handelt es sich um eine Adresse auf ihrem Server, zu der Facebook eine Notiz sendet, sobald ein Nutzer ihre Applikation authentifiziert. Genauso gibt es eine Post-Remove Callback-URL, an welche eine Notiz gesendet wird, sollte der Nutzer die Applikation wieder entfernt haben. Diese Events werden von dem Plugin allsamt behandelt, das heißt, Sie brauchen hier lediglich die Root-Adresse ihres Blogs samt Slash am Ende anzugeben.

Callback URLs definieren
Facebook Applikation: Callback URLs definieren

Als dritten Schritt müssen Sie nun die „Leinwandseite“ anlegen, also die Seite, über welche Facebook Nutzer auf das Blog Zugriff haben, sowie eine Leinwand Callback URL – das ist die eigentliche Seite, von welcher Facebook die Inhalte aus Ihrem Blog in die Leinwandseite integriert. Wiederum ist es wichtig, dass am Ende ein Slash ist, denn sonst funktionieren die internen Links im Blog nicht mehr.

Leinwandseite (Canvas) definieren
Facebook Applikation: Leinwandseite (Canvas) definieren

Dazu haben Sie noch ein paar weitere Optionen – nämlich wie Ihre „Leinwandseite“ integriert werden soll. Sie haben die Wahl zwischen Iframe und FBML. Bei FBML handelt es sich um ein eigenes Facebook XML-Schema, mit welchem Sie gewisse Tags verwenden können – etwa zum Anzeigen des Profilbildes eines Nutzers. Mit FBML können Sie auf sehr viele Facebook-Funktionalitäten zugreifen. Das WordBook-Plugin funktioniert jedoch über Iframes, da dabei die Nutzung von Javascript und gewissen Tags möglich ist, welche in dem FBML Schema nicht erlaubt sind. Das heißt, iFrames geben dem Anwendungsentwickler etwas mehr Flexibilität, jedoch kann über iFrames auf weniger Facebook-Funktionalitäten zurückgegriffen werden. Das hat auch den Vorteil, dass der Code, welcher Facebook von der Canvas Callback-URL holt, nicht noch einmal durch den Facebook-Parser geschickt werden muss, um die FBML-Tags darzustellen – ein kleiner Performance-Gewinn. Mit der Iframe Methode müssen lediglich von Facebook die Links angepasst werden, sodass sie über das Facebook Layout durch das Blog browsen können. Die Option „Resizeable“ erlaubt es dem Facebook Javascript, die Größe an das Layout von Facebook anzupassen.

Damit ist die Arbeit eigentlich schon fast getan. Jetzt muss lediglich das Plugin mit der WordPress-Standardmethode installiert werden, hochladen und aktivieren. Dann können Sie das Plugin über das Einstellungs-Panel konfigurieren. Dazu brauchen Sie zunächst ihre API-Kennung, ihren Entwickler-Schlüssel sowie die URL zu der Leinwandseite.

Einstellungen für das WordBook Plugin anpassen
Einstellungen für das WordBook Plugin anpassen

Und das war eigentlich schon alles.

Jetzt können Sie zudem noch einige Optionen aktivieren, beziehungsweise deaktivieren – etwa die Kommentarfunktion von Facebook aus oder ob Nutzer ihre Applikation zu ihrer Profilseite hinzufügen können. Insbesondere diese Funktionalität ist interessant, da dadurch die letzten vier bis fünf Posts in ihrem Blog auf der Profilseite ihrer Facebook-Leser erscheint. Da die Profilseite für die Freunde von Freunden in der Regel zugänglich ist, können Sie somit indirekt und einfach die Facebook-Profilseite nutzen, um Leserschaft für ihr Blog zu gewinnen.

Browsen durch das Blog über Facebook
Browsen durch das Blog über Facebook

Das WordBook Plugin behandelt also die Anfragen – im Endeffekt ließt Facebook so nur die Feeds ihres Blogs aus. Da ist ja noch nicht viel mit zu machen, könnte man sich fragen. Warum sollte man Facebook als Feedreader verwenden? (Und dafür auch noch Informationen an Dritte weitergeben, die man für eine Mitgliedschaft in der Leserschaft eines Blogs sonst nicht braucht). Die Frage ist berechtigt – doch die API kann noch mehr. ™

Applikation authentifizeren
Applikation authentifizieren

Morgen folgt ein zweiter Teil: Facebook in ein WordPress Blog integrieren.

Kategorien
Webdesign

Workshop Webservices: Amazon Webservices

Auch Amazon stellt Webservices für das deutsche Angebot zur Verfügung. Über diese Webservices können komplette und komfortable Partner Shops erstellt werden. Diese können auf einfache Art mit PHP und XSL in die eigenen Seiten integriert werden.

Bevor man mit der Entwicklung von Amazon Webservices starten kann, sind eine Anmeldung und eine Entwicklerkennung nötig. Über diese versucht Amazon festzustellen, wer Webservices verwendet. Zudem kann auf der Amazon Website auch ein Developer-Kit für Webserviceentwickler herunter geladen werden.

Amazon bietet seine Webservices erst seit Ende 2003 auch für deutsche Entwickler an. Über diese Webservices können Websitebetreiber Inhalte und Features von Amazon in ihre eigene Seite integrieren.

Die Strategie lohnt sich nicht nur für Amazon, sondern auch für den Websitebetreiber. Durch die von ihm aus vermittelten Verkäufe erhält er eine Partnerprovision über das „normale“ Amazon Partnerprogramm. Aufgrund dessen sind die Nutzerzahlen von Amazon Webservices bereits stark gestiegen. Bereits ein Jahr nachdem die Webservices in den USA eingeführt wurden, gab es fast 30.000 Entwickler, die Webservices in ihre Anwendungen integrierten.

Nachdem man den so genannten Developer-Token, die Entwicklerkennung erhalten hat, kann man sich an die Nutzung der Webservices machen. Bei der hier verwendeten Technik werden Anfragen an das Amazon System über die URL gesendet; beantwortet werden die Anfragen an das System über XML. In der Fachsprache wird das eine REST-, im Gegensatz zu einer SOAP-Kommunikation genannt. Man benötigt keine SOAP Schnittstelle, sondern kann die Anfragen direkt über die URL senden. Jedoch ist die REST-Kommunikation nicht standardisiert und bringt auch Nachteile mit sich. Für die Einführung in Amazon Webservices sollte sie jedoch genügen.

Die folgende URL stellt eine allgemeine Anfrage an das Amazon System dar. Auf diese Art sind alle Anfragen über REST aufgebaut:

 http://xml-eu.amazon.com/onca/xml3?t=[Ihre Werber Kennung]&dev-t=[Ihr
    DeveloperToken]&[Suchart]=[Suchwort]&mode=
    [Produkt]&sort=[Sortierart]&offer=All&type=[Antworttyp]
    &page=[Seitennummer]&f=[Format]&locale=[Länderkennung]

Wie man sieht gibt es zahlreiche Parameter mit deren Hilfe die Amazon Produkte abgefragt werden. Haben Sie bereits ihren Developer-Token und ihre Werber Kennung können Sie zum Beispiel folgende Anfrage an Amazon senden (passen Sie die Adresse einfach an, und rufen Sie sie im Browser auf)

      http://xml-eu.amazon.com/onca/xml3?t=[Ihre Werber Kennung]&dev-t=[Ihr
    Developer Token]&KeywordSearch=php html&mode=de_books&sort=+pmrank&offer=All&type=
    lite&page=1&f=xml&locale=de

Screenshot

XML Antwort auf REST Anfrage im Ausschnitt

In der folgenden Liste sehen Sie mögliche Parameterwerte. Die ausführliche Liste finden Sie in dem bei Amazon herunterladbaren Developer-Kit.

[Suchart]

  • AsinSearch
    Suche nach Amazon Produkten über die ASIN Nummern; das sind die amazoninternen Nummern, über die die Produkte eindeutig gekennzeichnet sind.
  • AuthorSearch
    Sucht nach einem Autor und den von ihm veröffentlichten Produkten.
  • BrowseNodeSearch
    Die Suche nach einer Produktkategorie – diese Kategorien sind durch eindeutige Browsenodes gekennzeichnet. Die Browsenodes der jeweiligen Kategorien können sie über einen Besuch bei Amazon.de ermitteln. Für die Kategorie „PHP“ ist die Browsenode http://www.amazon.de/exec/obidos/tg/browse/-/1201356/.
  • KeywordSearch
    Suche nach bestimmten Produkten über Schlüsselwörter.

Es gibt noch weitere Sucharten. Hier die vier wichtigsten:

[Suchwort] Das Suchwort hängt von der gewählten Suchart ab. Bei einer AsinSearch ist das Suchwort eine ASIN Nummer, bei einer AuthorSearch eben der Name des Autors.

[Produkt]
Da Amazon nicht nur Bücher verkauft, kann hier angegeben werden, nach welchen Produkten der Amazon Produktpalette gesucht werden soll.

Produkt Produktkennung für Amazon Deutschland
Bücher books-de
Pop-Musik pop-music-de
Klassische Musik classical-de
DVD dvd-de
Video vhs-de
Software software-de
Computer-/Videospiele video-games-de
Zeitschriften magazines-de

[Sortierart]
Die Ergebnisse einer Anfrage können nach verschiedenen Mustern sortiert werden. Auch hier werden nur die wichtigsten Sortierarten vorgestellt; die Sortierarten können von Produkt- zu Produkt auch variieren. Die hier vorgestellten Sortierverfahren sind diejenigen, die bei allen Produkten anwendbar sind.

  • +pmrank
    Dabei werden die Produkte nach der von Amazon bevorzugten Reihenfolge aufgelistet.
  • +salesrank
    Die Produkte werden nach ihrem amazoninternen Verkaufsrang aufgelistet. Das meistverkaufte Produkt der Ergebnisliste steht an erster Stelle.
  • +reviewrank
    Die Produkte werden nach den Bewertungen der Käufer aufgelistet.
  • +price
    Die Produkte werden nach ihrem Preis sortiert; das günstigste erscheint an erster Stelle.

[Antworttyp]
Mit dem Antworttyp wird der Umfang der Antwort bestimmt. Es gibt zwei mögliche Parameter; zum einen „lite“, zum anderen „heavy“.

Bei dem „lite“ Parameter werden lediglich allgemeine Informationen über die Produkte übermittelt. Darunter sind die:

  • URL zum Produkt
  • ASIN
  • Produktname (Titel)
  • Kategorie
  • Autor
  • Veröffentlichungsdatum
  • Hersteller (Verlag)
  • Adressen zu verschiedenen Bildern
  • Verfügbarkeit
  • Preis

Wird dagegen „heavy“ angegeben, erhält der Entwickler nicht nur die oben genannten Daten übermittelt, sondern komplette Produktbeschreibungen sowie die Referenzen von Käufern. So ist wird es möglich, den kompletten Content in die eigenen Seiten einzubinden.

[Seitennummer]
Pro Anfrage werden nur zehn Ergebnisse zurückgegeben. Findet Amazon jedoch zu der Anfrage mehr als nur 10 Produkte, werden diese auf mehrere Ergebnisseiten verteilt.

Gibt man nun als Seitennummer anstatt 1, zum Beispiel 5 an, werden als Ergebnis die Produkte zurückgegeben, die sich als Ergebnisse der Anfrage auf Ergebnisseite 5 befinden.
Zudem wird bei jeder Anfrage an Amazon die Anzahl der gefundenen Produkte, sowie die Anzahl der Ergebnisseiten übermittelt. Auf diese Weise kann man später eine Navigation durch die Ergebnisseiten erstellen.

[Format]
Über diesen Parameter wird das Format der Antwort von Amazon bestimmt. Standardmäßig ist dies das XML-Format. Man kann jedoch auch eine Transformierung der XML-Daten über ein eigenes XSLT-Stylesheet vornehmen. Im Ergebnis erhält man dann „reinen“ HTML-Code.

[Länderkennung]
Die Länderkennung gibt an, auf welches Amazon zugegriffen wird. Ist die Länderkennung „de“, wird auf das deutsche Amazon zugegriffen. Bisher gibt es nur wenige Länder in denen Amazon Webservices angeboten werden – ein Ausbau soll aber, laut Amazon, bald erfolgen.

Nachdem die einzelnen Parameter genauer spezifiziert sind, können wir uns an die Entwicklung eines einfachen Amazon Webservices machen. Dabei soll eine einfache Anfrage über Keywords an Amazon.de gerichtet werden. Diese soll von Amazon beantwortet und direkt über ein XSL-Stylesheet transformiert werden. Das Resultat sollte einfacher HTML Code sein.

Nehmen wir das Beispiel von oben:

      http://xml-eu.amazon.com/onca/xml3?t=[Ihre Werber Kennung]&dev-t=[Ihr
    Developer Token]&KeywordSearch=php html&mode=de_books&sort=+pmrank&offer=All&type=
    lite&page=1&f=xml&locale=de

Dabei wird nach Büchern über die Stichworte „php html“ gesucht. Die Antwort als XML-Daten können, wie bereits gesagt, auch direkt von Amazon über ein XSL-Stylesheet zu HTML transformiert werden. Hierzu muss der Format-Parameter bei der HTTP-Anfrage angepasst werden; dieser muss auf das XSL-Stylesheet verweisen, dass auf ihrem Server abgelegt ist.

Zunächst soll jedoch ein XSL-Stylesheet entwickelt werden. Betrachtet man sich die XML Rückgabe von Amazon erkennt man, dass der Wurzelknoten des Dokumentes <ProductInfo> ist. Zudem finden sich in der Antwort von Amazon auch die Request-Informationen, also die Anfrage die an Amazon gestellt wurden. Es werden pro Anfrage auf einer Seite maximal zehn Produkte angegeben.

Da die Anfrage den Antworttyp „lite“ verwendet, sind die Antwortdaten nicht sonderlich umfangreich. Alle Daten über ein Produkt sind in den jeweiligen <Details> Knoten gespeichert. Auf diese soll nun über das XSL-Stylesheet zugegriffen werden.

amazon_ws_1.xsl

      <?xml version="1.0" ?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.w3.org/TR/xhtml1/strict">
    <xsl:template match="/">
    <table border="1">
    <tr><td>ASIN</td>
    <td>URL</td>
    <td>Name</td></tr>
    <xsl:for-each select="ProductInfo/Details">
      <tr>
    <td><xsl:value-of select="Asin"/></td>
    <td><xsl:value-of select="@url"/></td>
    <td><xsl:value-ofselect="ProductName"/></td>
    </tr>
    </xsl:for-each>
    </table>
    </xsl:template>
    </xsl:stylesheet>

Wie man im XSL-Stylesheet gut erkennen kann, werden lediglich die drei Elemente ProductName, die ASIN und die Adresse unter welcher man das Buch bei Amazon bestellen kann, ausgegeben. Dies geschieht über eine for-each Anweisung; das heißt die Daten werden zu allen zehn Büchern der Ergebnisliste ausgegeben.

Lädt man dieses Stylesheet auf den Server und ändert seine Anfrage an Amazon wie folgt ab, dann erhält man eine mehr oder minder schön formatierte HTML-Ausgabe ohne XML.

      http://xml-eu.amazon.com/onca/xml3?t=[Ihre Werber Kennung]&dev-t=[Ihr
    Developer Token]&KeywordSearch=php html&mode=de_books&sort=+pmrank&offer=All&type=
    lite&page=1&f=http://www.deineseite.de/amazon.xsl&locale=de

Das heißt die ganze „Arbeit“ des Auslesens und Formatierens kann über XSL-Stylesheets durch Amazon gemacht werden lassen. Das ist natürlich sehr komfortabel.

Screenshot

So könnte das Ergebnis der XSL Transformation aussehen

Nun soll die Anfrage an den Amazon Webservice über ein PHP-Skript gesendet werden und die Antwort von diesem aufgefangen und ausgegeben werden. Das PHP Programm besteht aus nur wenigen Zeilen:

amazon.php

      <?php 
      $request = "http://xml-eu.amazon.com/onca/xml3?t=[Ihre Werber
    Kennung]&dev-t=[Ihr Developer Token]&KeywordSearch=php html&mode=de_books&sort=+pmrank&offer=All&type=
    lite&page=1&f=http://www.deineseite.de/amazon.xsl&locale=de;
      if (!$data = readfile($request)) {
    echo "Ein Fehler ist aufgetreten!";
    }
    else {
    echo $data;
    }
    ?> 

Dabei wird ein Trick angewendet – die Anfrage erfolgt über einen Dateiaufruf. Es wird die Anfrageadresse über die PHP-Dateifunktion readfile() geöffnet und eingelesen. Am Resultat ändert sich nichts, dennoch hat man so eine Schnittstelle zwischen PHP und Amazon Webservices auf Basis der REST Kommunikation geschaffen.

Die Nutzung der Amazon Webservices wird durch die Anwendung von XSL-Stylesheets um einiges flexibler – auch der Programmieraufwand wird stark verringert. Möchten Sie etwa einen kleinen Büchershop zu einem bestimmten Thema erstellen, so können Sie diverse XSL-Stylesheets für die verschiedenen Produktdarstellungen anwenden. Sie finden bei Amazon bereits ein solches XSL-Stylesheet, mit dem die verschiedensten Suchtypen angewendet werden können und über XSL formatiert ausgegeben werden.

Eigene Anwendungen
Die erste kleine Anwendung ist eine simple Zufallsausgabe von Büchern. So sollen beim Besuch einer Seite jeweils möglichst verschiedene Bücher automatisch vorgestellt werden. Hierzu wird auf die bereits im vorherigen Teil vorgestellte Technik zurückgegriffen. Dabei werden in einem Array verschiedene ASIN Nummern hinterlegt; durch eine Zufallszahl wird eine dieser ASIN Nummern ausgewählt, über den URL aufgerufen und durch ein XSL-Stylesheet formatiert und ausgegeben.

      <?php
$asin = array(0 => "3827262909",
1 => "3540413715",
2 => "3446227350",
3 => "3898424901"
);
$bid=mt_rand(0,count($asin)-1);
      $request = "http://xml-eu.amazon.com/onca/xml3?
t=[Ihre Werber Kennung]&dev-t=[Ihr DeveloperToken]&AsinSearch=".$asin[$bid]."
&mode=books&offer=All&type=lite&page=&f=
http://www.ihredomain.de/style.xsl&locale=de";
      if
($data = readfile($request)) {
echo $data;
}
else {
echo "Timeout";
}
?>

Wie sie im Skript sehen, kann es auch ab und an zu Timeouts kommen. Das ist insbesondere dann der Fall, wenn Amazon Webservices beziehungsweise der XSL-Prozessor auf den Amazon Servern stark ausgelastet sind. Des Weiteren hat Amazon eine „eine Sekunden“ Regel eingeführt, um die Serverauslastung zu minimieren. Demnach dürfen nur maximal eine Anfrage pro Sekunde an die Amazon Webservices gesendet werden. Bei starker Auslastung sollte man die Suchergebnisse cachen.

Das XSL-Stylesheet für das Beispiel könnte wie folgt aussehen:

      <?xml
version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:apply-templates select="ProductInfo/Details"/>
</xsl:template>
<xsl:template match="Details">
<div style="float:left; width: 400px; padding:3px; border: 1 dotted #000000;
margin:5px; background:#F5F5F5;">
<a href="{@url}" target="_blank"><image src="{ImageUrlMedium}"

alt="{ProductName}" align="left"/></a>
<b><xsl:value-of select="ProductName"/></b><br/><br/>
Preis: <xsl:value-of
select="OurPrice"/><br/><br/>
<i><a href="{@url}" target="_blank">Jetzt bei
Amazon.de kaufen!</a></i></div>
</xsl:template>
</xsl:stylesheet>


Das mögliche Ergebnis

Das obige Beispiel könnte man noch weiter umwandeln. So liessen sich etwas Bücher nach verschiedenen Themenbereichen ausgeben, um damit die Verkaufsrate zu steigern.

Ein eigener Amazon Shop
Hier gibt es bereits jede Menge kommerzielle Software, da das Potenzial für die Generierung von Umsätzen groß ist. Man findet komplette auf Amazon basierende Webshops die Umsätze im fünfstelligen Bereich generiert haben. Das lohnt sich für Amazon, aber auch für den Betreiber des Partnershops.

Für Webseitenbetreiber, die zu einem bestimmten Thema Inhalte liefern, kann sich das „anbieten“ von zum Thema passenden Büchern über einen Amazon Partnershop lohnen. Das haben sich auch bereits viele Software-Entwickler gesagt; diese stellen ihren Kunden Software für den Aufbau eines eigenen Amazon Partnershops zur Verfügung und lassen sich das auch entgelten.

Bei Associatesshop können Sie ihren eigenen Onlineshop auf Basis der Amazon Webservices erstellen. Dabei wird dieser Shop von Associateshop gehostet; sie müssen also keine Arbeit bei der Entwicklung des Shops verrichten. Die Sache hat jedoch einen Haken – für 80% der Produktpalette von Amazon wird ihre Partner-ID vom Amazon Partnerprogramm eingetragen, für die restlichen 20% der Produkte wird die Partner-ID von Associateshop eingetragen – so finanziert sich dieser Service.

Es gibt seit einiger Zeit auch kostenlose Funktionsbibliotheken, mit denen Sie sich Ihren Amazon Partnershop relativ schnell gestalten können – hierbei ist insbesondere die kostenlose AmazonLib vorzustellen, welche als Sourceforge Projekt herunter geladen werden kann.

Diese Bibliothek enthält sowohl Funktionen für die Nutzung von Amazon Webservices über REST- als auch SOAP-Kommunikation. Dabei werden die zurückgegebenen Daten in assoziative Arrays überführt – diese haben im Großen und Ganzen die gleiche Form.

Die folgende kleine Anwendung soll zeigen, in wie weit Sie einen einfachen Amazon-Shop mit Hilfe dieser Funktionsbibliothek aufbauen können.

Nehmen wir an unsere Webseite beschäftigt sich mit Themen wie Webdesign, HTML, CSS und so weiter. Unser kleiner Partnershop soll es möglich machen, auf die Amazon Angebote zu diesem Thema zuzugreifen respektive nach bestimmten Büchern zu suchen.

Zunächst müssen wir die Browsenodes der verschiedenen Themenbereiche ermitteln; über diese möchten wir später unser Menü gestalten.

Bereich Browsenode
Webdesign 1201364
HTML 1201246
Usability 1201380

Für unsere Anwendung benötigen wir eine Funktion, mit der der Besucher durch die BrowseNodes nach Bücher stöbern kann. Zudem benötigen wir eine Suchfunktion und eine Funktion über die die detaillierte Produktbeschreibung angezeigt werden kann.
Durch die AmazonLib wird das Senden und Empfangen von Daten kinderleicht.

Eine Suche nach einem Keyword kann über folgende Zeile Code durchgeführt werden:

      $Results = $AS->AmazonSearch(KEYWORD, $keyword, $Type,
'books', 1);

Wobei $AS ein Objekt ist, $keyword das Suchwort, nachdem gesucht wird, $Type die Rückgabeform (heavy oder lite) und die ‚1‘ am Ende die ausgewählte Suchergebnisseite.

Die einzige Schwierigkeit bei der Erstellung unseres kleinen Shops besteht darin, dass wir die Form der zurückgegebenen Arrays ermitteln müssen und dann die passenden Daten ausgeben.
Zunächst muss jedoch das Objekt erzeugt werden:

      //einbinden
des REST Parsers
require_once("amazonrest.php");
//die Werber-ID
$Tag = '[Ihre WerberID]';
$Token = '[Entwicklerkennung]';
//Rückgabetyp
$Type = 'heavy';
//Länderkennung
$Locale='de';
$Debug = false;
      //erzeugen des Objekts
$AS = new AmazonREST($Token, $Tag, $Locale, $Debug);

Ein kleines Menü für die drei verschiedenen Browsenodes könnte man mit folgenden Zeilen erstellen:

      $nodes = array ('Webdesign' => '1201364',
'HTML' => '1201246',
'Usability' => '1201380');
foreach ($nodes as $key=>$elem) {
print '<a href=amazon_shop.php?node='.$elem.' title='.$key.'>'.$key.'</a><br>';
}

Klickt nun ein Besucher auf einen so generierten Link, sollten die Bücher in der jeweiligen Kategorie aufgelistet werden.
Das könnte mit folgendem Codeschnipsel erledigt werden:

      if(isset($_GET['node']))
{
$Results = $AS->AmazonSearch(BROWSENODE, $_GET['node'], $Type, 'books', 1);
for ($i=0;$i<count($Results);$i++) {
print '<p>';
print '<img src='.$Results[$i]['ImageUrlMedium'].'
alt='.$Results[$i]['ProductName'].' align=left><br><a href=?asin='.$Results[$i]['Asin'].'>'.
$Results[$i]['ProductName'].'</a>';
print $Results[$i]['Reviews']['CustomerReview']
[0]['Summary'].'<br><br>';
print 'für nur '.$Results[$i]['OurPrice'].'</p>';
print '<small><a href=?asin='.$Results[$i]['Asin'].'>Weitere Informationen</a></small><br>';
print '<a href= '.$Results[$i]['url'].'>Bestellen!</a>';
}

Das schwierigste an einer solchen Anwendung ist zu entscheiden, welche Teile der Produktbeschreibung angezeigt werden sollen, und welche nicht – über die PHP Funktion var_dump() können Sie die Struktur des zurückgegebenen Arrays leicht ermitteln und dann auf diese zugreifen.

Genauso kann man auch mit der ASIN Suche vorgehen. Die ASIN wird wie in den Zeilen oben angedeutet als Parameter in dem URL übergeben. Über $_GET[‚asin‘] kann man ermitteln ob eine ASIN vorhanden ist, und dann die Daten zu dem Produkt ausgeben.

Alles in allem wird die Nutzung von den mitunter sehr komplexen Amazon Webservices anhand der AmazonLib oder anderen Funktionsbibliotheken erheblich erleichtert.

Kategorien
Webdesign

Workshop Webservices: Eigene Webservices entwickeln

Der eigene Webservice auf Basis von SOAP muss kein Traum bleiben. So gelingt der Einstieg…

Die Entwicklung eigener Webservices mit PHP ist dank der NuSoap Klasse gar kein Problem. Zunächst wollen wir einen einfachen Webservice konstruieren. Er wird vom Client aufgerufen und gibt lediglich einen Text zurück, der daraufhin ausgegeben wird.

Dazu benötigen wir an erster Stelle einen Server, der die Anfragen sinngemäß beantwortet. Ein solcher Server könnte wie folgt aussehen:

server.php

 <?php
    //NuSoap Bibliothek wird eingebunden
    require_once('NuSoap.php'); 
      //ein neuer Server wird erstellt
    $server = new soap_server; 
      //Die Funktion wird für den Server registriert
    $server->register('ausgabe'); 
      //die Funktion selbst
    function ausgabe ()
    {
    return "Text";
    }
    //Alle Anfragen werden an die Service-Methode weitergeleitet
    $server->service($GLOBALS['HTTP_RAW_POST_DATA']);
    exit();
    ?>

Der Aufbau des Servers ist schnell erklärt. Zunächst wird die NuSoap Bibliothek eingebunden. Daraufhin wird ein neuer Server erzeugt. Für diesen Server wird die Funktion „ausgabe“ registriert.

Ein solcher Prozess ist notwendig, da man sonst nicht zwischen den Funktionen unterscheiden könnte, die zum einem dem Client zugänglich sein sollen oder zum anderen nur für die Funktionalität des Webservices benötigt werden. Somit wird ein gewisses Maß an Sicherheit gewährleistet. Die letzte Zeile des Skriptes ist deshalb auch die wichtigste.

      $server->service($GLOBALS['HTTP_RAW_POST_DATA']);

Durch sie wird definiert, dass alle ankommenden SOAP Anfragen an die Service-Methode von NuSoap übergeben werden; diese verarbeitet die SOAP Anfrage und ruft die entsprechenden Funktionen auf (in unserem Beispiel die Funktion „ausgabe()“). Mit dieser Anweisung wird der Server initialisiert.

Zu dem Webservice gehört nun auch ein Client, der den Webservice aufruft. Dieser ist im folgenden Skript modelliert:

client.php

      <?php
    require_once('NuSoap.php');
    //erzeugen eines neuen Clients
    $client = new soapclient("http://www.ihredomain.de/webservice/server.php");

    echo $client->call('ausgabe');
    ?>

Der Code des Clients ist auch schnell erklärt. Zunächst muss die NuSoap Klasse eingebunden werden. Über new soapclient(„http://www.ihredomain.de/webservice/server.php“); wird ein neuer Client erzeugt.

Komplexere Webservices
Nun, da die ersten Schritte gemacht sind, können wir etwas mehr riskieren. Im nächsten Versuch geht es um ein kleines Contentsharing-Programm, mit dessen Hilfe Sie auf ihrer Website anderen Webmastern Ihren Content anbieten können.

Im Beispiel sind Sie der Anbieter von News-Inhalten. Diese sind in einer MySQL Datenbank hinterlegt. Über den Webservice möchten Sie es den Webmastern ermöglichen ihren Content einzubinden. Hierzu müssen diese Clients erstellen können, über welche diese zum Beispiel die aktuellen Schlagzeilen laden können.

Die Datenbanktabelle könnte wie folgt aussehen:

Füllen Sie eine solche Tabelle einfach mit ein paar Testwerten, damit Sie den Webservice „ausprobieren“ können. Um eine reale Anwendung zu modellieren, sollten Sie den Webservice auf ihrem eigenen Webserver ihrer Domain anlegen; den Webservice können Sie dann über einen Client auf ihrem „daheim“ Computer ansprechen.

Der Webservice
Der Webservice benötigt nun eine Funktion um die Datenbankverbindung aufzubauen und wieder zu schließen. Des Weiteren müssen die SQL-Anfragen nach den Wünschen der Clients modelliert werden; so soll der Client Abfragen nach einer bestimmten News-ID, sowie nach einer bestimmten Anzahl der aktuellsten Nachrichten stellen können.

Anhand des Beispiels werden auch einige Probleme bei der Verwendung von NuSoap ersichtlich.

Der Webservice Server könnte wie folgt aussehen…

      <?php
    require_once('NuSoap.php');
    $server = new soap_server;
    $server->register('news'); 
      function news($id, $count) {
    $fnews = new Cnews;
    if ($id != 0) {
    return $fnews->get_news_by_id($id);
    }
    if ($count != 0) {
    return $fnews->get_news_by_count($count);
    }
    } //end func
      $server->service($GLOBALS['HTTP_RAW_POST_DATA']);
    exit(); 
      class Cnews {
      //Verbindung mit DB und Auswahl der DB
    function connect() {
    $host = "localhost";
    $user = "benutzer";
    $password = "passwort";
    $db = "datenbank";
      mysql_connect($host, $user, $password);
    mysql_select_db($db);
    } //end func
      
    //eine bestimmte Anzahl $count an News wird ermittelt
    function get_news_by_count($count) {
    $this->connect();
    $sql = "Select * From news order by id DESC LIMIT 0,".$count;
    $res = mysql_query($sql);
    $i=0;
       while ($erg = mysql_fetch_array($res, MYSQL_ASSOC)) {
    $data[$i]['id'] = $erg['id'];
    $data[$i]['date'] = $erg['date'];
    $data[$i]['title'] = $erg['title'];
    $data[$i]['teaser'] = $erg['teaser'];
    $data[$i]['content'] = $erg['content'];
    $i++;
    } //end while
      mysql_close();
    return $data;
    } //end func
      
    //eine bestimmte News wird nach ihrer $id ermittelt
    function get_news_by_id($id) {
      $this->connect();
      $sql = "Select * From news where id=".$id;
    $res = mysql_query($sql);
    $i=0;
    while ($erg = mysql_fetch_array($res, MYSQL_ASSOC)) {
    $data[$i]['id'] = $erg['id'];
    $data[$i]['date'] = $erg['date'];
    $data[$i]['title'] = $erg['title'];
    $data[$i]['teaser'] = $erg['teaser'];
    $data[$i]['content'] = $erg['content'];
    $i++;
    } //end while
    mysql_close();
    return $data;
      } //end func
    } //end Class
    ?>

Es fällt auf, dass im Server eine Klasse Cnews definiert wird. Dies liegt daran, dass NuSoap in unserem Fall nicht mit den zwei Dimensionalen Arrays klar kommt; hierzu müsste man sich stärker in die Funktionsweise von NuSoap und auch die WSDL einarbeiten. In unserem Beispiel werden einfache Datentypen anhand der Informationen aus der Datenbank erstellt – mit diesen kann NuSoap umgehen.

Der Server selbst besteht nur aus einer Funktion „news“, an die zwei Parameter übergeben werden. Diese stellen unsere Auswahlmethoden dar – sollen also zum einen die jeweils aktuellsten News (die Anzahl wird durch den Paramter „count“ bestimmt) zurückgegeben werden, bzw. nur eine bestimmte News-Nachricht (bestimmt durch den Parameter ID).
Daraufhin wird ein Objekt der Klasse Cnews erstellt, über welches man auf die Methoden get_news_by_id($id) bzw. get_news_by_count($count)zugreifen kann. Über diese werden die zwei verschiedenen Abfragen gemacht, und die Ergebnisse in ein zweidimensionales Array überführt. Dieses Array hat die Form:

      $data[i][element]

Wobei i die Nummer des Datensatzes ist; wird ein Datensatz per ID ausgelesen, so ist i=0, werden drei Datensätze ausgelesen, so kann i entweder 0, 1 oder 2 sein. Über element wird auf die einzelnen Datenbankfelder zugegriffen.

Um nun die Funktionsweise zu verstehen, betrachten wir einen Client, der den Webservice konsumieren möchte.

      <html>
    <body>
    <?php
    require_once('NuSoap.php');
    $client=new soapclient('http://www.ihredomain.de/webservice/server.php');
    
      $param = array ('id' => '1',
    'count' => '0');
    $response = $client->call('news', $param);
    var_dump($response);
    echo '<h1>'.$response[0]['title'].'</h1>';
    echo '<p><b>'.$response[0]['teaser'].'</b></p>';
    echo '<p>'.$response[0]['content'].'</p>';
    ?>
    </body>
    </html>

Bei diesem Client wird die Neuigkeit aus der Datenbank mit der id=1 ermittelt. Auffallend ist, dass bei den Parametern die an die Funktion news des Webservices übergeben wird auch der Parameter „count“ definiert ist. Dieser hat den Wert 0. Das liegt an ebenfalls an gewissen Einschränkungen die NuSoap mit sich bringt. Würde man beispielsweise folgenden Client definieren:

      <html>
    <body>
    <?php
    require_once('NuSoap.php');
    $client=new soapclient('http://www.ihredomain.de/webservice/server.php');
    
      $param = array ('count' => '3');
    $response = $client->call('news', $param); 
      foreach ($response as $key=>$elem) {
    echo '<h1>'.$elem['title'].'</h1>';
    echo '<p><b>'.$elem['teaser'].'</b></p>';
    echo '<p>'.$elem['content'].'</p>';
    }
    ?>
    </body>
    </html>

So würde uns NuSoap die Nachricht mit der id=3 zurückgeben, anstatt den drei aktuellsten Einträgen in der Datenbank. Das liegt daran, dass NuSoap als Bibliothek bei der Verwendung von einfachen Arrays nicht zwischen den Indizes unterscheidet. Der erste übergebene Parameter ist für NuSoap immer eine ID. Das obige Beispiel funktioniert also nur wenn man

      $param = array ('id' => '0',
    'count' => '3');

an die Funktion „news“ übergibt. Deshalb wird im Server bei der Funktion news auch die folgende Fallunterscheidung gemacht:

      if ($id != 0) {
    return $fnews->get_news_by_id($id);
    }
    if ($count != 0) {
    return $fnews->get_news_by_count($count);
    }

Damit wird sichergestellt, dass die jeweils richtige Methode aufgerufen wird.

Wenn Sie sich die SOAP Nachrichten anschauen möchten, so können Sie dies ebenfalls im Client machen. Um besseres Debugging betreiben zu können sind können sie sich die SOAP Messages über echo $client->request; bzw. echo $client->response; ausgeben lassen.

In diesem letzten Teil des Webservices Workshop wurde nur ein kleiner Teil der Möglichkeiten, die dem Webworker durch das Programmieren eigener Webservices ermöglicht werden, vorgestellt. Auch konnte nur ein kleiner Teil der komplexen Welt des Zusammenspiels von PHP und SOAP über NuSoap vorgestellt werden.

Für interessierte möchte ich das Buch Webservices mit PHP aus dem Galileo Computing Verlag von Christian Wenz und Tobias Hauser vorschlagen; es bietet einen praktischen Einstieg in die Erstellung und Nutzung eigener Webservices. Des Weiteren empfiehlt sich die Webseite Xmethods, dort finden Sie zahlreiche andere Webservices, die Sie für ihre Website über PHP und NuSoap nutzbar machen können.

Kategorien
Webdesign

Workshop: Einführung in Webservices

Der Begriff Webservices könnte das Internet in den kommenden Jahren prägen. Doch was sind Webservices überhaupt, wie funktionieren sie und wie kann sie ein Webworker nutzen?

Webservices sind Dienste, die über das Internet bereitgestellt werden. Trotz dieser scheinbar recht einfachen Definition, haben sich die „Großen“, Microsoft, IBM, Hewlett Packard, Sun und andere. noch nicht auf eine verbindliche Definition geeinigt. Als kleinster gemeinsamer Nenner kann man sagen, dass über Webservices dem Nutzer (Client) Funktionen, Daten und Anwendungen bereitgestellt werden können. Dabei ist der Client nicht mehr der vor seinem Computer sitzende Surfer – es handelt sich um eigenständige Anwendungen, welche Webservices für bestimmte Programmfunktion nutzen.

Ein erstes Beispiel dafür sind die Google Webservices, die Ihnen wahrscheinlich schon einmal begegnet sind. Spezielle Services, die Google nutzen, aber von Dritten und nicht von Google bereitgestellt werden. Ein Programmierer kann über Webservices auf mächtige Funktionen Googles zugreifen und diese direkt in seine eigenen Anwendungen integrieren. So können zum Beispiel siteinterne Spezialsuchmaschinen ohne viel Aufwand erstellt werden. Genau das haben wir zu einem späteren Zeitpunkt auch vor.

Über Webservices kann man folglich komplette Anwendungen modellieren. Doch wie funktioniert die Kommunikation zwischen den verschiedenen Anwendungen? Die Daten müssen ja in irgendeiner Form von einem Programm zum anderen geschickt werden.

Das versenden der Daten wird über das HTTP-Protokoll bewerkstelligt. Anfragen und Antworten werden in Form von XML gesendet. Man hat sich auf diese Standards geeinigt, da sie quasi überall „verstanden“ werden. Sie sind plattform- und insbesondere programmiertechnisch unabhängig.

Anfragen an den Webservice werden in XML verpackt und über HTTP versendet. Die XML-Nachricht wird vom Webservice interpretiert und wiederum in Form von XML beantwortet.

Da Webservices für die Integration von Programmfunktionen in anderen Anwendungen gedacht sind, musste man sich auch hier auf eine einheitliche Form der Datenübertragung zum Nutzen aller einigen. So entstand SOAP, das so genannte Simple Object Access Protocol, basierend auf XML. Dadurch erhalten Anfragen und Antworten eine einheitliche Form. Zwar gibt es auch Webservices, die nicht auf SOAP zurückgreifen, dennoch wird sich SOAP in Zukunft durchsetzen, da nur durch eine einheitliche Datenübertragung die Nutzung von Webservices eine allgemeine Bedeutung erlangen kann.

Ein Anfrage besteht, wie man im folgenden Diagramm sehen kann aus einem festen Grundgerüst.

Struktur einer SOAP Nachricht

Das Grundgerüst ähnelt dem einer HTML-Seite, bestehend aus einem Head- und einem Body-Bereich, in dem Antwort- und Anfragedaten abgelegt sind.

Die Abbildung unten stellt ein einfaches Schema für den Datenfluss bei der Nutzung von Webservices dar. Dabei besitzt die Anwendung welche auf den Webservice zurückgreift ebenfalls einen XML Interpreter, über welchen die XML Daten in ein Format überführt werden, in welchem sie weiterverarbeitet werden können. Die SOAP Antwort muss von der Anwendung verarbeitet werden. Auf diese Weise wird die Nutzung von Webservices nicht nur auf einzelne Programmiersprachen beschränkt. Man kann Webservices über jede Programmiersprache verwenden, die eine XML- beziehungsweise SOAP-Schnittstelle besitzt.

Einfaches Datenflussdiagramm für die Nutzung von Webservices

Man kann dieses einfache Modell erweitern, indem man sich den Webservice auf den man zurückgreift selbst als eine Anwendung vorstellt, welche auf verschiedene andere Webservices zurückgreift.

Dies lässt sich an einem einfachen Beispiel veranschaulichen. Bei einer Buchbestellung möchten Sie den Status ihrer Bestellung verfolgen. Sie können jetzt direkt beim Verkäufer den Status bis zum Versand betrachten. Beim Zusteller (Post, UPS …) können Sie den weiteren Verlauf im Auge behalten.
Durch Webservices ist es nun möglich, dass der Verkäufer den Status der Bestellung auch über die eigenen Systemgrenzen verfolgen kann, indem er auf einen Webservice des Zustellers zurückgreift und dort den Status des Versands ermitteln und diesen an den Käufer weitergeben kann. Aber das ist nur ein sehr einfaches Beispiel der Integration von Webservices in andere Anwendungen.

Auch für den Programmierer ergeben sich Aspekte, die er zu beachten hat. Woher weiß der Programmierer auf welche Funktionen er bei der Nutzung von Webservices zurückgreifen kann und welche Parameter brauchen diese?

Hierzu wurde von IBM und Microsoft gemeinsam die Webservice Description Language, kurz WSDL, entwickelt. Sie basiert ebenfalls auf XML und ermöglicht die standardisierte Beschreibung von Webservices – sie wird auch vom W3C als möglicher Standard diskutiert.

Ein weiteres Problem besteht darin, dass bei einer Vielzahl von Webservices unklar ist, wer überhaupt was anbietet. Die Webservices müssen irgendwo verzeichnet sein. Idealerweise sollte der Webservicenutzer nur noch eine Anfrage an das Webserviceverzeichnis machen müssen; dieses leitet ihn dann direkt an den für die Anfrage passenden Webservice weiter. Ein solches „Telefonbuch“ für Webservices wurde durch UDDI (Universal Description, Discovery and Integration) geschaffen. In ihm sind die Webservice Anbieter und die von ihnen angebotenen Webservices verzeichnet. Bereits viele Anbieter haben UDDI implementiert.

Doch was für Chancen oder Zukunftsaussichten liegen in Webservices? Der Nutzen für Unternehmen ist eindeutig, da sie bei ihren Anwendungen – einfach gesagt – nicht mehr alles selbst programmieren müssen. Die jeweiligen Firmen werden sich durch ihre Webservices stärker als bisher vernetzen und so insbesondere in den Beziehungen zu den Kunden verbessern.

Und was bedeutet das für den Internetnutzer? Eine Vision besteht darin, dass man über Webservices eine Personalisierung des ganzen Internets bewerkstelligen könnte. So hat Microsoft bereits über sein .NET Passport einen ersten Schritt dahin getan. Über einen .NET Passport kann man auf die verschiedenen Angebote Microsofts zugreifen ohne jedes Mal ein neuen Account anlegen zu müssen. Das heißt, man muss seine persönlichen Daten nur noch in einem virtuellen Pass hinterlegen, über welchen man dann auf die unterschiedlichsten Internetdienste zugreifen kann. Heftige Diskussionen darüber sind ebenfalls im Gange, da mit einer solchen Praxis erhebliche Sicherheitsbedenken einhergehen.

Webworker können Webservices bisher nur im beschränkten Maße nutzen. Wie man das in Angriff nimmt, möchten wir Ihnen später in einem mehrteiligen Workshop nahe bringen. Lernen Sie, wie man die Webservices von Amazon und Google zum Wohle der eigenen Seiten einsetzen kann.

Kategorien
Webdesign

Workshop Webservices: Google Webservices mit PHP nutzen

Auch Google stellt seine Dienstleistungen über Webservices Entwicklern zur Verfügung. Angepasste Suchergebnisse können ohne weiteres in eigene Projekte eingebunden werden. Zur Hilfe kommt PHP.

Zwar befinden sich die Google Webservices immer noch in einer Testphase, doch findet man im Internet bereits zahlreiche Anwendungen. Zum Beispiel die „Übersuchmaschine“ Fast Forward, die jede Menge mehr aus Google herausholt. Oder Douwe Osingas fantasievolle Google Hacks.

Um mit der Entwicklung von Anwendungen mit Webservices zu starten, sollte man sich zunächst die Entwickler API bei Google herunterladen – sie enthält auch eine ausführliche Dokumentation.

Weiterhin muss man sich als Entwickler einmalig registrieren. Man erhält man eine Entwicklerkennung per Email, die bei jeder Google Webservice Implementierung verwendet werden muss.

Die Nutzung von Google über Webservices ist für den Privatanwender eingeschränkt. So sind nur maximal 1000 Suchanfragen pro Tag möglich, bei welchen maximal 10 Ergebnisse angezeigt werden. Für kleine Webseiten lohnt sich dies jedoch bereits – da man die Google Suchtechnik für die eigene Homepage kostenlos verwenden kann.

Google verwendet bei seinen Webservices das SOAP Protokoll (Simple Object Access Protocol), welches die Kommunikation zwischen Anwendungen auf Basis von HTTP ermöglicht. Dabei werden Anfragen und Antworten der Applikationen in einer normierten Form dargestellt. SOAP ist Teil einer W3C Empfehlung für die Konstruktion von Webservices und ein Kernelement von Microsofts .NET Architektur.

Für die Nutzung von SOAP mit PHP gibt es mit NUSoap von Dietrich Ayala eine mächtige PHP-Klasse die kostenlos heruntergeladen werden kann. Dabei werden die Parameter, die für den Webservice relevant sind – bei der Google Suche ist das insbesondere der Suchstring – an ein Objekt der NUSoap Klasse übergeben. Diese macht aus den Parametern eine in SOAP verfasste Suchanfrage und übermittelt diese an den Google Webservice. Dieser beantwortet nach Verarbeitung der Anfrage ebenfalls über eine SOAP Nachricht. Sie wird wiederum von NUSoap in ein bearbeitbares Objekt überführt.

Die Struktur beziehungsweise der Datenfluss bei der Nutzung von Google Webservices ist im folgenden Diagramm gezeichnet – anlehnend an das allgemeine Diagramm aus Teil 1 des Workshops. Dabei stellen die roten Pfeile den Datenfluss der Suchanfrage dar, die blauen Pfeile denjenigen der von der Google Datenbank zurückgegebenen Ergebnisse.

Screenshot
Datenfluss bei Google Webservices (vereinfacht)

Mithilfe von NUSoap wird die Handhabung von Webservices mit PHP um einiges vereinfacht. Man kann NUSoap auch mit vielen anderen Webservices verwenden. Man muss also nicht unbedingt SOAP beherrschen, um Webservices nutzen zu können.

Eine einfache Suchanfrage an Google mit PHP und NUSoap könnte wie folgt aussehen:

google_ws1.php

  <?php
    include ("nusoap.php");
    //der Suchstring
    $query = "drweb";
    //neuer SOAP Client, an welchen die Anfragen gesendet werden.
    $client = new soapclient("http://api.google.com/search/beta2");
    //die Parameter die Google für die Anfrage benötigt, in Form eines
    Arrays.
    $parameter['key'] = "Ihre Entwicklerkennung";
    $parameter['q'] = $query; //die Suchanfrage
    $parameter['start'] = 1;
    $parameter['maxResults'] = 10;
    $parameter['filter'] = false;
    $parameter['restrict'] = "";
    $parameter['safeSearch'] = false;
    $parameter['lr'] = "lang-de";
    $parameter['oe'] = "";
    $parameter['ie'] = "";
    $suchergebnis = $client->call("doGoogleSearch", $parameter, "urn:GoogleSearch");
    echo "<pre>";
    var_dump($suchergebnis);
    echo "</pre>";
    ?>

Über new soapclient() wird ein neues Objekt der Klasse NUSoap erzeugt. Dem Objekt wird dabei gleich die Adresse zum Webservice übergeben. Für eine Google Suchanfrage sind verschiedene Suchparameter notwendig. Diese werden in Form eines Arrays $parameter gespeichert.

Die Suche wird durch den Aufruf der Google Suchfunktion über $client->call(„doGoogleSearch“, $parameter, „urn:GoogleSearch“); durchgeführt. Das Suchergebnis wird durch die NUSoap Klasse in ein Array überführt und durch die var_dump() Funktion ausgegeben.

Die einzelnen Parameter, die für eine Suchanfrage benötigt werden sind:

  • key
    Die Entwicklerkennung, die man bei der Anmeldung für die Nutzung der Google Webservices per Email zugesendet bekommen hat.
  • q
    Der Suchstring, bzw. der Query – der Suchstring kann frei gewählt werden. Es gibt neben den „normalen“ Suchanfragen auch spezielle – so kann man beispielsweise die Suche auf die eigene Domain beschränken. Dies soll weiter unten näher erläutert werden.
  • start
    Die Suchergebnisseite, welche angezeigt werden. Gibt es zu einer Suche z.B. 170 Ergebnisse, so sind diese auf 17 Seiten zu jeweils 10 Ergebnissen unterteilt. Wird start auf zwei gesetzt, so werden die Suchergebnisse auf der zweiten Ergebnisseite angezeigt.
  • maxResults
    Die Anzahl der maximalen Suchergebnisse, die ermittelt werden sollen. Der Wert kann aufgrund von den Begrenzungen von Google nur zwischen 1 und 10 liegen.
  • filter
    Die Frage, ob z.B. gleiche oder ähnliche Suchergebnisse ausgefiltert werden sollen.
  • restrict
    Über den restrict Parameter kann man die Suche auf bestimmte Überbegriffe oder Länder beschränken. Ein Überbegriff kann z.B. Windows oder auch PHP sein.
  • safeSearch
    Über diesen Parameter kann man einstellen ob nicht jugendfreie Suchergebnisse aus der Ergebnisliste herausgefiltert werden sollen oder nicht.
  • lr
    Über den lr-Parameter wird die Sprache der Suchergebnisse bestimmt. Mit dem Parameter lang-de kann man festlegen, dass nur deutschsprachige Suchergebnisse angezeigt werden.
  • oe und ie
    ie und oe stet für „input“ bzw. „output“ encoding. Dabei werden diese Parameter jedoch in der neueren Entwicklungsversion der Google Webservices ignoriert – dennoch müssen für sie in der Suchanfrage vorhanden sein. Der Parameter war dafür gedacht, dass über ihn die Codierung der Anfragen und der Ergebnisse angegeben werden kann und somit verschiedene Sprachen unterstützt werden könnten – standardmäßig wird von einer UTF-8 Codierung ausgegangen.

Was macht NuSoap aus den Parametern?
Für Interessierte ist es vielleicht gut zu wissen, wie NuSoap die Schnittstelle zwischen SOAP und PHP darstellt. An NuSoap werden alle notwendigen Informationen übergeben; dieser wandelt die Informationen in eine SOAP Anfrage um und sendet diese an Google.

Die SOAP Anfrage – der so genannte SOAP Envelope- aus unserem Beispiel sieht wie folgt aus:

      
    POST /search/beta2 HTTP/1.0
    User-Agent: NuSoap/0.6.8 (1.76)
    Host: api.google.com
    Content-Type: text/xml; charset=ISO-8859-1
    SOAPAction: "urn:GoogleSearch"
    Content-Length: 988

    <?xml version='1.0' encoding='UTF-8'?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
    <SOAP-ENV:Body>

    <ns1:doGoogleSearch xmlns:ns1="urn:GoogleSearch" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <key xsi:type="xsd:string">[die Entwicklerkennung]</key>
    <q xsi:type="xsd:string">drweb</q>
    <start xsi:type="xsd:int">1</start>
    <maxResults xsi:type="xsd:int">10</maxResults>
    <filter xsi:type="xsd:boolean">false</filter>
    <restrict xsi:type="xsd:string"></restrict>
    <safeSearch xsi:type="xsd:boolean">false</safeSearch>
    <lr xsi:type="xsd:string">lang-de</lr>
    <ie xsi:type="xsd:string"></ie>
    <oe xsi:type="xsd:string"></oe>
    </ns1:doGoogleSearch>

    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

Diese Anfrage wird von Google wiederum über SOAP beantwortet.

Möchte man sich die SOAP Anfrage oder die SOAP Antwort im Browser anschauen, so kann man diese über echo $client->request; bzw.echo $client->response; ausgeben lassen; dabei handelt es sich um in NuSoap integrierte Methoden.

Das Suchergebnis
Das Suchergebnis beinhaltet maximal 10 Ergebnisse. Dabei liefert Google zu jedem Suchergebnis detaillierte Informationen. Diese werden durch NuSoap in Arrays gespeichert. Dabei handelt es sich um ein zweidimensionales Array – es sind zwei Arrays ineinander verschachtelt. Im ersten Array befinden sich die Details zur Suche zum Beispiel Anzahl der Suchergebnisse, Suchdauer und so weiter. Im zweiten Array befinden sich die Details zu den einzelnen Suchergebnissen.

Das Array $suchergebnis aus dem Beispiel enthält folglich folgende Informationen:

  • $suchergebnis[‚documentFiltering‘]
    Ein boolscher Wert der angibt, ob die Suchergebnisse gefiltert wurden.
  • $suchergebnis[’searchComments‘] Kommentar zu er durchgeführten Suche; z.B. wenn Wörter wie „und“, „oder“ etc. aus der Suchanfrage entfernt worden sind.
  • $suchergebnis[‚estimatedTotalResultsCount‘]
    Die geschätzte Anzahl der Suchergebnisse.
  • $suchergebnis[‚estimateIsExact‘]
    Ein boolscher Wert, der angibt, ob die geschätzte Zahl an Suchergebnissen exakt ist.
  • $suchergebnis[‚resultElements‘]
    In diesem Array befinden sich die einzelnen Suchergebnisse und die Details zu ihnen.
  • $suchergebnis[’searchQuery‘]
    Der Suchstring.
  • $suchergebnis[’searchTips‘]
    Eine Zeichenkette, die dem Benutzer Tipps gibt, wie die Suche optimiert werden kann.
  • $suchergebnis[‚directoryCategories‘]
    Ein Array, in welchem zu der Suche passende OPD Kategorien angegeben sind.
  • $suchergebnis[’searchTime‘]
    Die Suchdauer..

Das Array $suchergebnis[‚resultElements‘] enthält die einzelnen Suchergebnisse. Sie sind numerisch im Array hinterlegt. Auf das erste Suchergebnis könnte man folglich mit $suchergebnis[‚resultElements‘][0] zugreifen. Zu jedem Einzelnen Suchergebnis sind in dem Array folgende Daten gespeichert:

  • summary
    Ist das Suchergebnis im Open Directory Project (OPD) eingetragen, so wird der Text der zu dem Link im OPD steht übergeben.
  • URL
    Der URL zu dem Suchergebnis wird als Zeichenkette übergeben.
  • snippet
    Ein Auszug aus der gefundenen Seite, die die gesuchten Wörter beinhaltet. Diese sind mit HTML fett markiert.
  • title
    Der Titel der Seite, ebenfalls bereits über HTML formatiert.
  • chachedSize
    Die gechachte Größe der Seite in Kilobytes.
  • relatedInformationPresent
    Ein boolscher Wert, ob verwandte Webseiten gefunden wurden.
  • hostName
    Werden ähnliche Suchergebnisse ausgefiltert (ist der filter Paramter in der Suchanfrage auf true gestellt), so erscheinen nur zwei Ergebnisse von dem Host. Der Hostname ist hierbei als String gespeichert.
  • directoryCategory
    Die Kategorie, in welcher die Seite im OPD angegeben ist.
  • directoryTitle
    Der Titel unter welchem die Seite im OPD erscheint.

Screenshot
Suchanfrage bei Google – die relevanten Daten sind markiert

Möchte man folglich den Titel des ersten Suchergebnisses ausgeben, so könnte man dies über echo

      $suchergebnis['resultElements'][0]['title']; 

machen.

Google Suchmaschine für die eigenen Seiten…
Binden wir Google nun in ein eigenes Projekt ein. Benutzer sollen die Möglichkeit haben im ganzen Web oder nur auf einer bestimmten Seite zu suchen.

Die Suche wird über ein einfaches Formular aufgerufen. Dieses Formular übergibt den Suchstring, den Query an das Skript, die daraufhin über NuSoap eine Anfrage an Google sendet und die Antwort formatiert ausgibt.

Das Skript könnte wie folgt aussehen:

suche.php

      <?php
    include ("NuSoap.php");

    function google_search($query) {
    //neuer SOAP Client, an welchen die Anfragen gesendet werden.
    $client = new soapclient("http://api.google.com/search/beta2");
    //die Parameter die Google für die Anfrage benötigt, in Form eines
    Arrays.
    $parameter['key'] = "[Entwicklerkennung]";
    $parameter['q'] = $query; //die Suchanfrage
    $parameter['start'] = 1;
    $parameter['maxResults'] = 10;
    $parameter['filter'] = false;
    $parameter['restrict'] = "";
    $parameter['safeSearch'] = false;
    $parameter['lr'] = "lang-de";
    $parameter['oe'] = "";
    $parameter['ie'] = "";

    $suchergebnis = $client->call("doGoogleSearch", $parameter, "urn:GoogleSearch");
    return $suchergebnis;
    }

    $host = "www.drweb.de";
    $query = $_POST["query"];
    $search_type= $_POST["search_type"];

    if (isset($query)) {
    if ($search_type == "local") {
    $query = "site:".$host." ".$query;
    }
    $suchergebnis = google_search($query);

    echo "<h3>Suche nach:".$suchergebnis['searchQuery']."</h3>";
    echo "<h4>Suchdauer: ".$suchergebnis['searchTime'].",
    Suchergebnisse ".$suchergebnis['estimatedTotalResultsCount']."</h4>";

    foreach ($suchergebnis['resultElements'] as $elem) {
    //Ausgabe der Suchergebnisse
    echo "<p><b><a href=".$elem['URL'].">".$elem['title']."</a></b>
    <br\>".$elem['snippet']."<br /><small>".$elem['URL']."</small></p>";
    }
    }
    //da $query leer ist, wird das Formular ausgegeben
    else {
    ?>
    <form method="post" action="suche.php">
    Suchen nach...<br />
    <input type="text" name="query" value="Suche"/><br
    />
    nur auf dieser Seite...
    <input type="radio" name="search_type" value="local"
    checked="checked"/>
    <br /> das ganze Web...
    <input type="radio" name="search_type" value="all"
    /><br />
    <input type="submit" value="Senden"/><br />
    </form>
    <?php
    }
    ?>

Die Funktion, durch die die Suche durchgeführt wird, sieht eigentlich genau so aus, wie im ersten Beispiel. Lediglich der Suchstring wird über die Funktion als Parameter übergeben. Dieser wird im Formular eingegeben und über POST gesendet.

Ist $query jedoch gesetzt, so muss überprüft werden, ob es sich um eine seiteninterne Suche handelt, oder um eine Suche im ganzen Web. Dies wird durch die Radiobuttons ausgewählt. Handelt es sich um eine siteinterne Suche, so wird der $query so angepasst, dass nur Suchergebnisse von der Domain durchsucht werden.

Bei Google kann man über den Suchstring site:www.drweb.de html nur die Seite www.drweb.de nach dem String durchsuchen. Entsprechend wird die Funktion google_search($query) aufgerufen. Daraufhin wird das Suchergebnis über eine Foreach-Schleife formatiert ausgegeben.

Bei einer Suche nach „html kurs“ im Internet könnte das Ergebnis dann so aussehen:

Screenshot
Formatierte Ausgabe der Google Suchergebnisse

Es gibt zahlreiche Möglichkeiten, die Ausgabe speziellen Wünschen anzupassen. Lohnenswert ist die Benutzung von Google als siteinterne Suchmaschine auf jeden Fall – sie erhöht das Usability der Seite für den Benutzer ohne großen Aufwand für den Sitebetreiber.

Kategorien
Webdesign

AJAX in der Praxis: Ein einfacher Chat

Ein Online-Chat braucht beispielsweise Java. Mit der Integration verschiedener Techniken in AJAX ist es jedoch möglich, einen einfachen Onlinechat auf schnelle Weise in eine gewöhnliche Webseite zu integrieren.

Ein schlichter Onlinechat ist eigentlich schwierig zu realisieren, da die neu eingegangenen Messages stets vom Server geladen werden müssen. Dies bedeutete bisher, dass die Seite neu geladen werden musste. Um dies zu umgehen, hat man auf andere Möglichkeiten gesetzt, wie zum Beispiel JavaApplets. Mit AJAX wird die Sache erleichtert, da man die Seiten nicht mehr neu im Browser laden muss. Die periodische Überprüfung nach neuen Messages kann über JavaScript und XMLHttpRequests mit der setTimeOut-Funktion gesteuert werden.

Hört sich einfach an – ist es im Grunde auch so. Bei Dr.Web wurde bereits einmal von mir ein Skript vorgestellt, mit dem auf einfache Art eine Shoutbox programmiert wurde. Dieses Skript soll nun Schritt-für-Schritt AJAX tauglich gemacht werden und zu einem Chat umfunktioniert werden.

Im Internet gibt es bereits OpenSource-AJAX-Chat-Anwendungen, zum Beispiel Lace und Treehouse. Unser Beispiel wurde entwickelt, um im Rahmen dieser Artikelserie AJAX näher zu bringen. Das Beispiel weist längst nicht alle Features auf, die möglich wären, ist so aber besser verständlich.

Rollen wir das Feld von hinten auf und beginnen wir mit dem so genannten Server Backend. In unserem Fall ist das eine MySQL Datenbank. Für unsere kleine Anwendung verwenden wir eine Tabelle Namens „ajaxChat“, welche die Felder id, name, nachricht und date hat; diese kann mit folgender SQL-Anweisung erzeugt werden:

  CREATE TABLE `ajaxChat` (
    `id` LONGINT NOT NULL AUTO_INCREMENT ,
    `name` VARCHAR( 60 ) NOT NULL ,
    `nachricht` VARCHAR( 120 ) NOT NULL ,
    `date` VARCHAR( 30 ) NOT NULL ,
    PRIMARY KEY ( `id` )
    );

Screenshot

Zunächst wird das Backend angegangen; zum einen die Datenbank, zum anderen das PHP-Skript, welches die XML-Antworten auf die http-Anfragen erzeugt.

Das PHP-Skript (nennen wir es ajaxchat.php) auf der Serverseite muss folgende Funktionalitäten aufweisen:

  • Verbindung zur Datenbank öffnen und schließen.
  • Nneue Einträge müssen in die Datenbank eingefügt werden können.
  • Neue Einträge müssen abgeholt werden können; als Erkennung dient eine eindeutige ID.

Die Ausgabe der Daten, das heißt die asynchrone Antwort des Servers erfolgt über XML; dieses wird dann über JavaScript und CSS im Browser dargestellt.

Die Datenbankverbindung kann über folgenden Zeilen-Code geöffnet werden; die Variablen müssen an die jeweilige Serverumgebung angepasst werden.

       <?php
    //wichtig; damit nicht gecached wird!
    header( "Cache-Control: no-cache, must-revalidate" );
    header( "Pragma: no-cache" );
    //damit unser JavaScript die Daten auch als XML erkennt
    header("Content-Type: text/xml");

    $db_host = "localhost"; // Host
    $db_user = "root"; // User
    $db_password = ""; // Passwort
    $db_name = "test"; // Datenbank

    mysql_connect($db_host,$db_user,$db_password) or die(mysql_error());
    mysql_select_db($db_name) or die(mysql_error()); 

Nachrichten auslesen
Die Funktion, mit der stets die aktuellsten Nachrichten aus der Datenbank extrahiert werden:

      function getLatestEntries($latestID) {

    $query = "SELECT id, name, nachricht, date FROM ajaxchat WHERE id > $latestID
    ORDER BY id DESC LIMIT 20";
    $erg = mysql_query($query);
    echo "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
    echo "<messages>";
    while($erg2=mysql_fetch_array($erg)) //Erzeugung der XML Ausgabe
    {
    echo "<message><id>".$erg2['id'];
    echo "</id><name>".$erg2['name'];
    echo "</name><nachricht>".$erg2['nachricht'];
    //Formatierung des Timestamps
    echo "</nachricht><date>".date("d.m.Y H:i",$erg2['date']);
    echo "</date></message>";
    }
    echo "</messages>";
    }

Bei dieser Funktion kommt es insbesondere auf die SQL-Abfrage an; über die Bedingung id > $latestID, werden lediglich die Einträge ermittelt, deren ID größer (d.h. deren Einträge „frischer“ sind), ermittelt. Die Variable latestID wird, wie wir weiter unten sehen werden, bei dem http Requests über JavaScript übergeben.

Durch den Zusatz ORDER BY id DESC in der SQL Abfrage wird sichergestellt, dass die Einträge absteigend ausgegeben werden; LIMIT 20 beschränkt die Ausgabe auf die letzten zwanzig Einträge. Die Erzeugung des XML-Codes erfolgt schlicht über die echo-Funktion (eine Verwendung der DOMXML Funktionen von PHP wäre bei dem geringen Aufwand nicht gerechtfertigt).

Neue Einträge
Das erzeugen von neuen Einträgen in die Tabelle erfolgt über folgende Funktion:

      function createNewEntry($name, $nachricht) {

    //HTML Tags entfernen
    $name = strip_tags($name, '');
    $nachricht = strip_tags($nachricht,''); 

    $query = "INSERT INTO ajaxchat(name, nachricht, date)
    VALUES ('$name','$nachricht','".time()."')";
    echo "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
    if (!mysql_query($query)) {
    //nicht notwendig, dient nur der Fehlerkontrolle
    echo "<createNewEntry>0</createNewEntry>";}
    else {
    echo "<createNewEntry>1</createNewEntry>";
    }
    }

Dabei erzeugt die Funktion createNewEntry wiederum eine XML-Antwort; dieser schenken wir aber keine Beachtung. Sie wurde lediglich zur Fehlerüberprüfung implementiert, so dass man über JavaScript, falls es einen MySQL-Fehler beim Erzeugen eines neuen Eintrag gäbe, dies entsprechend im Browser anzeigen könnte – wenn man denn wollte.

Skriptsteuerung
Da alle Anfragen an ein und dasselbe Skript geschickt werden (ajaxchat.php), benötigen wir noch ein paar Zeilen-Code, mit denen sichergestellt wird, dass bei den Aufrufen auch die jeweils angesprochene Funktion ausgeführt wird. Wir arbeiten hierbei mit Parametern, die in die http Anfragen über das XMLHttpRequest Objekt eingebaut sind.

      //falls action=createNewEntry, Erzeugung eines neuen Eintrages
    if ($_GET['action'] == "createNewEntry") {
    createNewEntry($_GET['name'], $_GET['nachricht']);
    }
    //falls action=getLatestEntries, Ausgabe der neuesten Einträge
    elseif ($_GET['action'] == "getLatestEntries") {
    getLatestEntries($_GET['latestID']);
    }
    mysql_close(); //schließen der PHP Verbindung
    ?>

Die AJAX Engine

Screenshot

Im zweiten Schritt wird die AJAX-Engine aufgebaut; mit ihr werden die http-Anfragen erzeugt und die Ergebnisse im Browser dargestellt

Die AJAX-Engine hat folgende Aufgaben:

  • Erzeugung der http-Anfragen um neue Nachrichten vom Server zu erfragen-
  • Erzeugung der http-Anfragen, über die neue Einträge in der Datenbank gespeichert werden.

Da später das Empfangen von Daten automatisiert wird beziehungsweise über die setTimeOut-Funktion gesteuert wird, ist es notwendig, dass wir für unsere Anwendung zwei XMLHttpRequest-Objekte erzeugen. Damit wird sichergestellt, dass man gleichzeitig sowohl Daten empfangen als auch senden kann.

Erzeugung zweier XMLHttpRequest-Objekte
Das JavaScript zur Erzeugung der XMLHttpRequest-Objekte ist identisch mit dem aus Teil 2 dieser Artikelserie:

       <script type="text/javascript">
    var latestID = 0; //latestID als globale Variable; wichtig!

    function createXMLHttpReqObj() { //erzeugt die XMLHttpRequest Objekte

    // für Mozilla etc.
    if(window.XMLHttpRequest) {
    try { //Fehler abfangen
    req = new XMLHttpRequest();
    } catch(e) {
    req = false;
    }
    // für den InternetExplorer
    } else if(window.ActiveXObject) {
    try {
    req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
    try {
    req = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
    req = false;
    }
    }
    }
    return req;
    }
    // Initialisierung der beiden Objekte
    var httpGetLatestEntries= createXMLHttpReqObj();
    var httpCreateNewEntry = createXMLHttpReqObj();

Neueste Einträge abfragen
Bei der Abfrage nach den neuesten Einträgen wird die LatestID in der http-Anfrage mit übergeben. Diese ist eine globale Javascript-Variable, da ihr Wert aus Funktionen heraus geändert wird. Initialisiert wird sie mit dem Wert null. Die URL zu dem Skript ajaxchat.php müssen Sie natürlich an Ihre Umgebung anpassen.

Die Funktion getLatestEntries sieht wie folgt aus:

      //Funktion, mit der Anfragen nach neuen Einträgen gesendet wird
    function getLatestEntries() {
    //Anfrage senden, wenn Status der letzten Anfrage "completed" ist, bzw. "nicht
    initialisiert" (d.h. erster Aufruf)
    if (httpGetLatestEntries.readyState == 4 || httpGetLatestEntries.readyState
    == 0) {
    //Übergabe der latestID
    httpGetLatestEntries.open("GET","
    http://localhost/ajax/ajaxchat.php?
    action=getLatestEntries&latestID="+latestID, true);
    httpGetLatestEntries.onreadystatechange = handleHttpGetLatestEntries;
    httpGetLatestEntries.send(null);
    }
    }

Als Funktion, mit welcher der Browser die Serverantwort behandelt ist die Funktion handleHttpGetLatestEntries angegeben:

      //Behandelt die Serverantwort
    function handleHttpGetLatestEntries() {

    //wenn Anfrage den Status "completed" hat
    if (httpGetLatestEntries.readyState == 4) {
    //ermitteln der Antwort
    response = httpGetLatestEntries.responseXML.documentElement;
    //ermitteln der Messages; Überführung in ein Array
    messages = response.getElementsByTagName('message');

    //wenn es mindestens eine neue Nachricht hat, dann wird diese angezeigt!
    if(messages.length > 0) {

    for (var i=messages.length-1; i>=0; i--) {
    //Darstellung im Browser mit dem DOM
    showEntry= document.getElementById("showEntries");
    neuSpan = document.createElement('span');
    neuSpan.setAttribute('class','entry'); //CSS Klasse
    neuSmall = document.createElement('small');
    neuNameDate = document.createTextNode(messages[i].getElementsByTagName
    ('date')[0].firstChild.nodeValue + ': ' + messages[i].getElementsByTagName('name')[0].firstChild.nodeValue
    +': ');
    neuSmall.appendChild(neuNameDate);
    neuSpan.appendChild(neuSmall);
    neuSpan.appendChild(document.createElement('br'));
    neuNachricht = document.createTextNode(messages[i].getElementsByTagName
    ('nachricht')[0].firstChild.nodeValue);
    neuSpan.appendChild(neuNachricht);
    neuSpan.appendChild(document.createElement('br'));
    showEntry.insertBefore(neuSpan, showEntry.firstChild);
    }

    //Festlegung der neuen latestID; Zugriff auf die Werte über das DOM
    latestID = messages[0].getElementsByTagName('id')[0].firstChild.nodeValue;

    }
    //erneuter periodischer Aufruf (eine Art Rekursion)
    setTimeout('getLatestEntries();',3000); //Erneute Anfrage in drei Sekunden
    }
    }

Über die setTimeOut-Funktion wird nun unsere periodische Überprüfung nach neuen Einträgen durch den Aufruf der Funktion getLatestEntries alle drei Sekunden initialisiert; dabei handelt es sich um eine Art „verzögerte Rekursion“.

Neue Nachrichten eintragen
Die JavaScript Funktionen, über welche neue Einträge an den Server gesendet werden, sind ähnlich aufgebaut, wie die vorherigen Funktionen. Mit der ersten Funktion createNewEntry wird die http-Anfrage erzeugt, wobei ihr die Werte für die Felder name und nachricht aus dem HTML-Formular übergeben werden.

      //neue Nachricht auf dem Server erzeugen; die Übergabe der Werte
    erfolgt über das HTML Formular
    function createNewEntry(name, nachricht) {

    //Anfrage senden, wenn Status der letzten Anfrage "completed" ist, bzw. "nicht
    initialisiert" (d.h. erster Aufruf)
    if (httpCreateNewEntry.readyState == 4 || httpCreateNewEntry.readyState ==
    0) {
    //URL für HTTP Anfrage; muss angepasst werden
    url = 'http://localhost/ajax/ajaxchat.php?action=createNewEntry&name='
    + name + '&nachricht=' + nachricht;
    httpCreateNewEntry.open("GET", url, true);
    httpCreateNewEntry.onreadystatechange = handleHttpCreateNewEntry;
    httpCreateNewEntry.send();
    }
    }
      //behandelt die Antwort des Servers
    function handleHttpCreateNewEntry() {
    if (httpCreateNewEntry.readyState == 4) {
    //nachdem eine neue Nachricht erfolgreich erzeugt wurde, wird diese angezeigt
    getLatestEntries();
    }
    }

Nachdem die Daten erfolgreich in der Datenbank hinterlegt wurden, wird die Funktion getLatestEntries() wiederum aufgerufen, um sicherzustellen, dass die neue Nachricht auch sofort angezeigt wird.

Das Frontend
Nachdem nun die AJAX-Engine aufgestellt ist, wenden wir uns dem Frontend zu. Auch hier ist JavaScript gefragt. So können die Eingaben über JavaScript überprüft werden; die Darstellung der Nachrichten kann über eine CSS-Klasse beeinflusst werden.

Das Formular
Das Formular ist simpel gehalten; es gibt lediglich Eingabefelder und einen Submit-Button:

      <form name="form1" method="post" action="">
    <p>Name<br />
    <input type="text" name="name" id="name">
    <br />
    <br />
    Nachricht<br />
    <input type="text" name="nachricht" id="nachricht">
    </p>
    <p>
    <input type="submit" name="Submit" value="Submit" onclick="checkInput(document.form1.name.value,
    document.form1.nachricht.value)" >
    </p>
    </form>

Nachdem man auf den Submit-Button geklickt hat, wird die JavaScript-Funktion checkInput aufgerufen; an diese werden die eingegebenen Daten weitergegeben. In ihr wird die Eingabe überprüft.

Eine solche Überprüfung könnte im einfachsten Fall wie folgt aussehen:

      function checkInput(name, nachricht) {
    if(name != "" && nachricht != "") {
    //Falls alles OK ist, kann der neue Eintrag erzeugt werden
    createNewEntry(name, nachricht);
    }
    else {
    alert("Bitte sowohl einen Namen, als auch eine Nachricht eingeben!");
    }
    }

Die Darstellung der Ergebnisse kann mit Hilfe von CSS beeinflusst werden. Dazu greifen wir auf die über das DOM zugewiesene CSS-Klasse „entry“ zurück; über folgende Zeilen könnte man die Darstellung im Browser ein wenig ansehnlicher machen.

      <style type="text/css">
    .entry {
    width: 100%;
    background-color: #F5F5F5;
    border: 1 dotted #666666;
    font-family: Verdana;
    }
    .entry#small {
    color: #CCCCCC;
    }
    </style>

Screenshot

AJAX Chat in der konkreten Anwendung

Der Aufruf des Skriptes kann über einen onLoad-Event im Body-Tag der HTML-Seite (<body onload=“getLatestEntries()“>), in welcher das Formular eingebettet ist, eingeleitet werden; dabei wird die Funktion getLatestEntries das erste mal aufgerufen, wodurch die setTimeOut-Endlosschleife gestartet wird.

Alles in allem, war es ein langer Weg von der Idee zur konkreten Umsetzung. AJAX bedeutet nicht nur, dass die Webseiten in Zukunft in ihrem Verhalten normalen Desktop-Anwendung ähneln können, sondern bringt auch viel Arbeit mit sich. Vor allem das Zusammenspiel von JavaScript und XML klappt bislang nicht immer reibungslos.

Das hier vorgestellte Beispiel besitzt zudem einen Bug, der nicht verschwiegen werden darf. Auf der JavaScript Seite kann nicht mit Umlauten oder anderen Sonderzeichen umgegangen werden; diese verursachen einen Fehler. Alle Dateien gibt es wie immer als Paket zum Download. ™

Zur Problematik der Sonderzeichen ein Feedback von Rene Gade: Ich arbeite seit längerem mit XMLHTTPRequest, um das Umlaut-Problem in den Griff zu bekommen, habe ich eine Funktion zwischen geschaltet, die die Variablen-Werte kodiert und zum Server schickt.

Beispiel:

    //url = Ziel
    //param = Name der Variable
    //val = Wert der Variable
    function AddParam(url, param, val)
    {
    var qs = (url.indexOf("?") > -1 ) ? "&" : "?";
    //kodiere Wert (Umlaute und Sonderzeichen )
    qs += param + "=" + encodeURI(val);
    return url + qs;
    }

Auf dem Server müssen die Werte je nach Sprache wieder dekodiert werden.

Erstveröffentlichung 19.09.2005

Kategorien
Webdesign

AJAX: Teil 2 – Das XMLHttpRequest Objekt

Die Idee zu AJAX gab es schon lange – erst mit dem XMLHttpRequest wurde eine browserübergreifende Möglichkeit eröffnet, um die asynchrone Kommunikation mit dem Server über JavaScript zu ermöglichen. Wir bitten zur Praxis.

Das XMLHttpRequest Objekt hat die asynchrone Kommunikation im AJAX Modell erst ermöglicht. Mit ihm kann man Daten im Hintergrund über JavaScript über http an Applikationen auf dem Server senden und die Antwort des Servers in Form von XML empfangen und verwerten. Die Vernetzung von JavaScript und serverseitiger Technologie wird über das Objekt ermöglicht und forciert.

Die Initialisierung funktioniert im Internet Explorer noch über ActiveX, während in anderen Browsern es als einfache Objektableitung geht. Eine einfache Javascript Funktion, mit der Ableitungen des Objektes erzeugt werden können und Anfragen an den Server verschickt werden, könnte wie folgt aussehen:

 var req;

    function loadXMLDoc(url) { //Übergabe der URL, an welche die Anfrage
    geschickt werden soll
    req = false;
    // für Mozilla etc.
    if(window.XMLHttpRequest) {
    try { //Fehler abfangen
    req = new XMLHttpRequest(); 
    } catch(e) {
    req = false;
    }
    // für den InternetExplorer
    } else if(window.ActiveXObject) {
    try {
    req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
    try {
    req = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
    req = false;
    }
    }
    }
    if(req) { //falls Objekt erzeugt werden konnte…
    //bei Aenderung des Status der Anfrage, wird Funktion handleReqChange aufgerufen
    req.onreadystatechange = handleReqChange; 
    req.open("GET", url, true);
    req.send(null);
    }
    }

Mit den „try-catch“ Bereichen wird versucht ein Objekt zu erzeugen; wenn es nicht funktioniert, wird der Fehler abgefangen. Es gibt sowohl eine Anweisung für Mozilla und Co. als auch den InternetExplorer.

Im dritten Teil des Codes wird überprüft, ob ein Objekt erfolgreich erzeugt werden konnte. Ist dies der Fall, so wird die Anfrage erzeugt und an die im Funktionsaufruf übergebene URL per HTTP abgesendet. Die zu übergebenden Daten können zum Beispiel als Parameter an den URL angehängt werden. Bei einer Veränderung des Status der Anfrage, wird über den Eventhandler onreadystatechange die Funktion handleReqChange aufgerufen. Dieser Programmcode ist für alles Weitere essentiell.

Bevor genauer auf das Beispiel eingegangen wird, sollten Sie sich jedoch einen Überblick über die Methoden und Eigenschaften des XMLHttpRequest Objektes machen.

Methoden

  • getAllResponseHeaders()
    Gibt die gesamte Antwort des Servers als Zeichenkette zurück.
  • getResponseHeader(„headerLabel“)
    Gibt die Antwort des Servers unter dem Header „headerlabel“ zurück.
  • open(„method“, „URL“[, asyncFlag[, „userName“[, „password“]]])
    Schreibt die Ziel URL fest; weitere optionale Eigenschaften können angefügt werden.
  • send(content)
    Sendet die HTTP Anfrage
  • setRequestHeader(„label“, „value“)
    Fügt Werte in den Header der Anfrage hinzu.
  • abort()
    Die aktuelle Anfrage (Aktion) wird abgebrochen.

Mit diesen Methoden können Daten in Form von HTTPRequests abgeschickt werden. Nachdem eine Anfrage an den Server geschickt wurde, stehen dem Objekt mehrere Eigenschaften zur Verfügung, mit denen man zum Beipiel den Status des HTTPRequests abfragen kann.

Eigenschaften

  • readyState
    Gibt den Status des Objektes zurück:
    0 = nicht initialisiert
    1 = Wird geladen
    2 = Geladen
    3 = Interaktiv (d.h. es interagiert mit dem Server)
    4 = Completed
  • onreadystatechange
    Event handler, mit dem man jede Änderung des Status des Objektes verfolgen kann; bei unserem obigen Beispiel wird bei einer Veränderung des Status der Anfrage die Funktion handleReqChange aufgerufen.
  • responseText
    Die String-Version der Antwort des Servers auf eine Anfrage
  • responseXML
    Über diese Eigenschaft kann man auf die XML kompatible Antwort des Servers zugreifen.
  • status
    Der Statuscode, der vom Server auf die Anfrage zurückgeschickt wurde (z.B. Status 200 – alles ok)
  • statusText
    Zeichenkette, klurze Nachricht, die zum Statuscode hinzukommt; z.B. bei Status 500 – Internal Server Error.

Unser erstes AJAX Beispiel scheint zunächst relativ simpel, zeigt jedoch Möglichkeiten, wie man AJAX praktisch nutzen kann. Nehmen wir einmal an, dass Sie irgendwo Ihre Emailadresse eingeben müssen beziehungsweise wollen. Nun soll über JavaScript zunächst überprüft werden, ob die Emailadresse korrekt ist. Daraufhin wird auf der Serverseite durch einen Datenbankabgleich überprüft, ob die Emailadresse eventuell bereits in der Datenbank hinterlegt wurde. CSS soll zur Darstellung von Fehlermeldungen verwendet werden; das DOM ist hierfür essentiell. Das alles soll geschehen, ohne dass die Seite neu aufgerufen werden muss.

Beginnen wir mit dem schlichten HTML-Formular und dem dazugehörigen CSS-Code an.

      <input id="email" name="email" type="text" onblur="checkEmail(this.value,'')"
  /><br />
  <span class="hidden" id="emailCheckFailed">
  Die Emailadresse befindet sich bereits in der Datenbank
  </span>
  <span class="hidden" id="emailSyntaxCheckFailed">
  Falsche Syntax
  </span>

Es gibt zwei versteckte Span-Felder, die zwei vordefinierte Fehlermeldungen anzeigen können. Der dazugehörige CSS-Code sieht wie folgt aus:

      <style type="text/css">
  span.hidden{
  display: none;
  }
  span.error{
  color: #FF0000;
  }
  </style>

Bei onBlur, also wenn das Feld den Fokus verliert, wird die Funktion checkEmail aufgerufen; ihr wird zudem noch der Wert des Textfeldes übergeben, der daraufhin überprüft wird. Der zweite übergebene Parameter ist für die weitere Behandlung wichtig, denn mit der Funktion checkEmail wird sowohl die Eingabe des Nutzers untersucht, als auch die Antwort des Servers auf die Anfrage behandelt. Zunächst ist die übergebene Antwort natürlich leer – die Emailadresse, die eingegeben wurde, wird mit JavaScript auf korrekte Syntax überprüft. Ist das nicht der Fall (das heißt, wenn checkEmailSyntax false zurückgegeben hat), dann wird das zweite Span-Feld über das DOM angesprochen und die Fehlermeldung „Falsche Syntax“ angezeigt.

      function checkEmail(input, response)
    {

    if (response != ''){ 
    //etwaige Fehlermeldung für falsche Email-Syntax wird versteckt
    message = document.getElementById('emailSyntaxCheckFailed');
    message.className = 'hidden';

    // die Antwort des Servers wird untersucht
    message = document.getElementById('emailCheckFailed');
    if (response == 1){ //falls die Emailadresse bereits in der Datenbank liegt
    message.className = 'error'; //Fehlermeldung wird angezeigt
    }else{ 
    message.className = 'hidden';
    } 

    }else{
    // zunaechst wird Emailadresse clientseitig auf korrekte Syntax ueberprueft
    if(checkEmailSyntax(input)) {
    //ist Syntax korrekt, dann wird Anfrage gesendet
    //der Parameter wird an den URL angehaengt
    url = 'http://localhost/checkEmail.php?email=' + input; 
    loadXMLDoc(url);
    }
    else {
    //ist Syntax nicht korrekt, so wird entsprechende Fehlermeldung angezeigt
    message = document.getElementById('emailSyntaxCheckFailed');
    message.className = 'error'; //Aenderung der CSS Klasse
    }
    }
    }

Hier erkennt man schön die Verzahnung der Technologien mit AJAX. Die Überprüfung der Syntax der Emailadresse kann ohne weiteres direkt mit Javascript auf der Clientseite erfolgen. Hierzu muss man nicht ein Skript auf dem Server belasten; andererseits findet der Datenbankabgleich auf dem Server statt. Für die Überprüfung der Email-Syntax wurde auf die Funktion von Jan Winkler in einem früheren Dr. Web Artikel zurückgegriffen.

So sieht’s aus, wenn die Emailsyntax nicht stimmt

So sieht’s aus, wenn die Emailsyntax nicht stimmt

Wie bereits oben erwähnt, muss überprüft werden, ob die Anfrage an den Server erfolgreich war. Dazu wird bei jeder Statusveränderung der Anfrage die Funktion handleReqChange aufgerufen. Hat die Anfrage den Status „completed“ erreicht und hat die Serverantwort den Status 200 (das heißt: alles okay), dann kann die XML Antwort des Servers verarbeitet werden. Die XML Antwort könnte zum Beispiel so aussehen:

      <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <response>
    <method>checkEmail</method>
    <result>1</result>
    </response>

Dabei wird berücksichtigt, dass in einem Skript auch mehrere Funktionen gleichzeitig aufgerufen werden können und so das Skript eventuell auch mehrere Ergebnisse zurücksendet. Deshalb wird der Name der Funktion auch übergeben.

Die Funktion handleReqChange, welche die Antwort behandelt, sieht wie folgt aus:

      function handleReqChange() 
    {
    // Wenn der Status der Anfrage "completed" ist
    if (req.readyState == 4) {
    // wenn die Server Nachricht den Statuscode 200 hat
    if (req.status == 200) {
    // hier wird die XML Antwort verarbeitet
    response = req.responseXML.documentElement;
    result = response.getElementsByTagName('result')[0].firstChild.data; //hier
    wird der Rueckgabewert ermittelt
    checkEmail('',result); //dieser wird zurueck an die Funktion checkEmail geleitet
    } else {
    alert("Beim Empfangen der XML Daten ist ein Fehler aufgetreten\n" + req.statusText);
    }
    }
    }
    
      response = req.responseXML.documentElement;

die Serverantwort angesprochen. Über

      result = response.getElementsByTagName('result')[0].firstChild.data;
    

wird die Antwort des Servers im XML-Tag <result> ermittelt.
Daraufhin wird die Funktion checkEmail erneut aufgerufen, wobei dieses mal der erste Parameter leer ist und der zweite (d.h. die Serverantwort) gesetzt ist. Entsprechend des Ergebnisses der Datenbankabfrage wird die Fehlermeldung angezeigt.

Nun fehlt nur noch das PHP-Skript, welches per Javascript angerufen wird. Hier kann man z.B. eine Textdatei öffnen und nach der Emailadresse suchen, oder etwa einen Datenbankabgleich machen.

      <?php
    header('Content-Type: text/xml');

    function checkEmail($email)
    { 
    /* hier wird der Datenbankabgleich gemacht */
    return 1; 
    }
    ?>
    <?php echo '<?xml version="1.0" encoding="UTF-8"
    standalone="yes"?>'; ?>
    <response>
    <method>checkEmail</method>
    <result><?php 
    echo checkEmail($_GET['email']) ?>
    </result>
    </response>

Wichtig ist, dass der Content-Type auf text/xml gesetzt wird, da sonst das XMLHttpRequest-Objekt nicht mit der Antwort zurechtkommt.

Nun, das war eine Menge Stückwerk. Es zeigt sich, dass AJAX doch recht kompliziert ist, da man nicht ein ganzes Programm hat, sondern verschiedene kleine Sinneinheiten, die nur gemeinsam optimal funktionieren. Das Beispiel an und für sich ist trivial, doch nur so gelingt der Einstieg ohne Probleme. Weitere AJAX Beispiele, bei denen deutlich wird, was man alles anstellen kann, finden sich bei Clearnova. Die dazugehörigen Quelldateien für unser Beispiel gibt es „in einem Guss“ zum Download, zum genauen Studium.

Und so sieht es schließlich aus:

Falls die Emailadresse bereits gespeichert ist – die Antwort hat man, ohne das die Seite neu geladen werden musste!
Falls die Emailadresse bereits gespeichert ist – die Antwort hat man, ohne dass die Seite neu geladen werden musste

Erstveröffentlichung 02.09.2005