Als kleines Anwendungsbeispiel erstelle ich gerade eine CRUD Anwendung, die eine Weboberfläche zur Verwaltung einer Liste von 'TPerson' erzeugt. Im Hintergrund arbeitet
Indy TIdHTTPServer sowie ein von mir erstelltes kleines Framework.
Die erste Version hat aber noch Designschwächen - abesehen davon dass sie nicht threadsicher programmiert ist. Ich überlege daher sie noch einmal zu überarbeiten, um sie möglichst klein und kompakt zu halten, aber das Softwaredesign zu verbessern.
Wenn mit dem Browser die Adresse
http://localhost/rest/persons aufgerufen wird, erzeugt der Server eine
HTML Seite mit einer Liste der (in einem Datenmodul) vorhandenen TPerson Objekten. Der Servercode ruft dazu eine Funktion aus dem Datenmodul auf, die den
HTML Code erzeugt und zurückgibt:
Delphi-Quellcode:
// GET http://localhost/rest/persons
// list all persons
Path('
persons');
Produces('
text/html');
GET(
procedure(Request: TRequest; Response: TResponse)
begin
Response.ContentText := CRUDModule.GetPersons;
Response.CharSet := '
UTF-8';
end);
Die Funktion TCRUDModule.GetPersons iteriert über ein TDictionary<string, TPerson> und erstellt eine
HTML Seite:
Delphi-Quellcode:
function TCRUDModule.GetPersons:
string;
var
P: TPair<
string, TPerson>;
begin
Result := '
<html>';
for P
in Persons
do
begin
Result := Result
+ '
<p>'
+ P.Value.
Name
+ '
- '
+ Format('
<a href="persons/%s">show</a>', [P.Key])
+ '
</p>';
end;
Result := Result + '
</html>';
end;
Der
HTML Code der TPerson-Liste enthält dabei auch einen Hyperlink auf die Resource http://
localhost/rest/persons/<id> wobei id jeweils der Key des Objekts im Dictionary ist.
Der Code der durch den Hyperlink-Klick ausgeführt wird, liest das id Attribut aus dem GET Request, und erzeugt über die Funktion CRUDModule.GetPerson
HTML Code zur Darstellung des TPerson Objekts:
Delphi-Quellcode:
// GET http://localhost/rest/persons/
// get person information
Path('
persons/{id}');
Produces('
text/html');
GET(
procedure(Request: TRequest; Response: TResponse)
var
ID:
string;
begin
ID := Request.Params.Values['
id'];
Response.ContentText := CRUDModule.GetPerson(ID);
Response.CharSet := '
UTF-8';
end);
Im nächsten Schritt könnte ich (über TCriticalSection) den Code threadsicher zu machen.
Dabei gibt es die Wahl zwischen zwei Orten:
entweder im Requesthandler:
Delphi-Quellcode:
// GET http://localhost/rest/persons
// list all persons
Path('
persons');
Produces('
text/html');
GET(
procedure(Request: TRequest; Response: TResponse)
begin
CS.Enter;
try
Response.ContentText := CRUDModule.GetPersons;
finally
CS.Leave;
end;
Response.CharSet := '
UTF-8';
end);
oder im Datenmodul:
Delphi-Quellcode:
function TCRUDModule.GetPersons:
string;
var
P: TPair<
string, TPerson>;
begin
Result := '
<html>';
CS.Enter;
try
for P
in Persons
do
begin
Result := Result
+ '
<p>'
+ P.Value.
Name
+ '
- '
+ Format('
<a href="persons/%s">show</a>', [P.Key])
+ '
</p>';
end;
finally
CS.Leave;
end;
Result := Result + '
</html>';
end;
Die zweite Variante hat den Vorteil, dass die Zeit der Sperrung des TDictionary minimiert wird. Die Critical Section soll ja nicht länger als notwendig den Zugriff anderer Threads auf die Daten blockieren.
Hat jemand noch andere Empfehlungen, umd das Design auf den "Stand der Technik" zu bringen? In einer 'richtigen' Anwendung (das Programm ist nur ein "Anwendungsbeispiel") würde ich zum Beispiel auch seitenweise Darstellung der Daten (Pagination) vorsehen.