When I began programming at my first company in a practical semester, the very first application I wrote, used TCP/IP to communicate with the existing applications. Almose every application I developed since, was using some kind of network communication to control another application or to be controlled by other applications. Over the years, I added other platforms (iOS and Android), which controlled the delphi applications. In every new project, I tried to tune the server side a little bit, to make my life easier.
Recently, I extended the server part to support multiple versions of an API.
The idea is that there is one IWebAPI interface, which represents the public API. The TAbstractAPI "implements" this interface. The real API classes inherit from this class and implement the methods they want. Having an abstract class between the interface and the implementation releases the real API classes from having to implement every method from the interface.
The API classes register themselves in a TWebAPIFactory, which will instantiate these classes when needed.
An example of an API
Another concept, which has been used in my previous applications is that the methods of the api are not called directly. Instead, Rtti is used to invoke the right method.
These concepts are glued together in the datamodule, that holds an IdHTTPServer.
It is a work in progess, so use it carefully!
Delphi XE3 is required.
You can get the source code from the git repository at http://code.google.com/p/delphi-webapi/
Recently, I extended the server part to support multiple versions of an API.
The idea is that there is one IWebAPI interface, which represents the public API. The TAbstractAPI "implements" this interface. The real API classes inherit from this class and implement the methods they want. Having an abstract class between the interface and the implementation releases the real API classes from having to implement every method from the interface.
The API classes register themselves in a TWebAPIFactory, which will instantiate these classes when needed.
An example of an API
unit WebAPI.Version1;
interface // empty interface section, I think Nick Hodges would like this ;-) (http://www.nickhodges.com/post/Getting-Giddy-with-Dependency-Injection-and-Delphi-Spring-5-Delphi-Spring-Basics.aspx)
implementation
uses
JSON, WebAPI.Abstract, WebAPI.Factory;
type
TWebAPIVersion1 = class(TAbstractAPI)
function ReverseString(jo: IJSONObject): string; override;
end;
{ TWebAPIVersion1 }
function TWebAPIVersion1.ReverseString(jo: IJSONObject): string;
var
ch: char;
begin
result := '';
for ch in jo.GetString('value') do
begin
result := ch + result;
end;
end;
initialization
begin
TWebAPIFactory.RegisterAPI('1', TWebAPIVersion1);
end;
end.
Another concept, which has been used in my previous applications is that the methods of the api are not called directly. Instead, Rtti is used to invoke the right method.
class function TWebAPIInvoker.Invoke(const aAPI: IWebAPI; const aCommand: string; aParam: IJSONObject): string;
var
o: TObject;
ctx: TRttiContext;
typ: TRttiType;
m: TRttiMethod;
begin
o := TObject(aAPI);
typ := ctx.GetType(o.ClassInfo);
m := typ.GetMethod(aCommand);
if assigned(m) then
begin
result := m.Invoke(o, [TValue.From<IJSONObject>(aParam)]).AsString;
end
else
raise Exception.Create('Unknown Command: "' + aCommand + '"');
end;
I used the IJSONObject from my JSON-Library as a parameter, because it has been proven to be a reliable format for passing complex data over the borders of applications. These concepts are glued together in the datamodule, that holds an IdHTTPServer.
procedure TdmWebserver.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
if isAPIRequest(ARequestInfo.Document) then
HandleAPIRequest(AContext, ARequestInfo, AResponseInfo)
else // Is File Request
HandleFileRequest(AContext, ARequestInfo, AResponseInfo);
end;
function TdmWebserver.isAPIRequest(const aDocument: string): boolean;
begin
result := aDocument.StartsWith('/api/');
end;
procedure TdmWebserver.HandleAPIRequest(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
documentParts: TArray<string>;
version, command: string;
api: IWebAPI;
i: integer;
begin
// e.g. /api/1/ReverseString
documentParts := ARequestInfo.Document.Split(['/'], TStringSplitOptions.ExcludeEmpty);
version := documentParts[1];
command := '';
for i := 2 to high(documentParts) do
begin
command := command + documentParts[i];
end;
api := TWebAPIFactory.newAPI(version);
AResponseInfo.ContentText := TWebAPIInvoker.Invoke(api, command, nil)
end;
It is a work in progess, so use it carefully!
Delphi XE3 is required.
You can get the source code from the git repository at http://code.google.com/p/delphi-webapi/
Keine Kommentare:
Kommentar veröffentlichen