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