Hey zusammen,
ich würde auch gerne ein Modul von mir vorstellen, wo ich mir denken kann, dass dies auch für den ein oder anderen Interessant sein könnte!
Beschreibung
Der HTTPManager ist eine Klasse, die GET/POST Requests annimmt und diese im Hintergrund (einzeln) hintereinander bearbeitet. (Für Dateidownloads ist diese Klasse eher weniger gedacht!)
Das besondere am HTTPManager ist, dass es komplett auf Interfaces gestützt ist, die nur COM Datentypen verwenden und so ideal in einem Pluginsystem eingebunden werden kann. So wird z.B. die HTTPManager Klasse in dem Hauptprogramm erstellt und das Interface an die Plugins weitergegeben. Als Strukturmuster diente die HTTP Komponente von Indy. Der HTTPManager nutzt das Strategie-Pattern um die letztendliche Komponente welche die Requests übernimmt (z.B. Indy) flexibel auszutauschen. Die Hintergrundarbeit wird mit der OmniThreadLibrary erledigt.
Es war mir außerdem wichtig, dass der Benutzer sich so wenig wie nötig mit Cookies, Parameterkodierungen oder z.B. Socks Konfigurationen beschäftigen muss. Es wurden diverse wichtige Konfigurationen direkt integriert, sinnvolle Standardwerte gesetzt und z.B. bei den POST-Parametern ein automatische abstrakte Parameterklasse integriert (wählt selbstständig ContentType).
Da die Kommunikation mit dem Internet aus Anwendungen heraus immer wichtiger wird und dies besonders bei Delphi Einsteigern gerne probiert wird, bietet diese Komponente das Ideale Handwerkszeug zur Realisierung. Natürlich gibt es in Delphi auch fertige Klassen um damit beispielsweise XML Requests zu basteln; aber diese beschränken sich dann halt immer auch bestimmte Voraussetzungen.
Ein kleiner Bonus: der HTTPManager speichert alle Aktivitäten über den Programmverlauf, sodass man auch zu späteren Zeitpunkten an gezielte Requests kommt oder dem Nutzer z.B. über eine visuelle Oberfläche eine Art HTTP Logger bietet (siehe Demos).
Die Funktionen erklären sich fast von selbst. Die Benutzung ist so gedacht, dass man einen GET/POST erstellt und dieser dann abgearbeitet wird. Der HTTPManager gibt dazu eine UniqueID zurück. Nun kommt auch die OmniThreadLibrary auf der Nutzerseite zum Einsatz. Durch einen einfachen Task wird in einer Schleife geprüft (HasResult) ob der Request bereits beendet wurde. Danach wird zum Hauptprogramm synchronisiert und über GetResult kommt man nun an die geladenen Daten.
Intern werden die GET/POST-Requests dupliziert, damit Zugriffe von einer DLL aus - nicht zu Problemen mit der Referenzzählung führen. Diese werden dann über ein ThreadPool (IOmniBackgroundWorker) abgearbeitet. Im Worker (IOmniWorkItem) wird dann eine Instanz der Indy HTTP Klasse erstellt und die Daten übergeben. Beim POST-Request wurden die Parameter in einer allgemeinen Klasse gesammelt. Diese werden dann noch auf die entsprechende Indy Klasse umgemapt.
Ist der Request abgeschlossen oder traten Fehler auf werden alle Informationen in einem IHTTPResult Interface zusammengefasst. Dieses und die eigentlichen Request Informationen werden dann gebündelt und als IHTTPProcess zusammengefasst. Somit hat man später nochmals Zugriff aus alle Daten, denn die Indy HTTP Klasse wird nach Ende des Requests wieder freigegeben.
Die IHTTPProcess-Interfaces speichert ein Array (welches über ein TOmniMREW gesichert ist).
procedure var // jeder Request hat eine eindeutige ID um diesen auch später wieder zu Identifizieren
RequestID: Double; begin // Basierend auf dem Singelton, wird hier die Instanz vom HTTPManager geholt
HTTPManager := THTTPManager.Instance();
// Der GET-Request wird erstellt und ausgeführt
RequestID := HTTPManager.Get(THTTPRequest.Create('http://www.google.de/search?q=http+indy'));
// In diesem Moment wird der GET-Request bereits beareitet
Parallel.Async( procedure begin repeat
sleep(50); // Einfache Schleife, die Prüft ob der Request bereits abgearbeitet wurde until HTTPManager.HasResult(RequestID); end,
Parallel.TaskConfig.OnTerminated( procedure(const task: IOmniTaskControl) var
HTTPProcess: IHTTPProcess; begin // Der Request ist vollständig und man hat nun Zugriff auf die Daten (dafür braucht man die ID)
HTTPProcess := HTTPManager.GetResult(RequestID); // Den Quellcode könnte man nun weiterverarbeiten oder einfach in ein Textfeld ausgeben
mGETResult.Lines.Text := HTTPProcess.HTTPResult.SourceCode; end
)); end;
GET-Request (FollowUp):
Nutzt man die HTTP-Komponente direkt, gehen die Cookies in dieser Sitzung nicht verloren (z.B. für ein Login). Beim HTTPManager müsste man alle Cookies und eventuell weitere Informationen aus dem vorherigen Request laden und dem neuen übergeben. Um den Arbeitsaufwand gering zu halten sind die GET und POST Methoden entsprechend überladen - somit reicht die Angabe der vorherigen RequestID um Cookies und Sitzungsspezifische Informationen automatisch zu übernehmen:
procedure var
PrevRequestID, RequestID: Double; begin // ein GET-Request wird erstellt und ausgeführt - beendet mit <PrevRequestID>
// Darauf aufbauend wird nun ein neuer Request erstellt:
RequestID := HTTPManager.Get('http://www.google.de/search?q=neue+suche+mit+login', PrevRequestID);