Browse Source

* OpenAPI helper units and classes

Michaël Van Canneyt 8 months ago
parent
commit
ede14da4d2

+ 13 - 1
packages/fcl-web/fpmake.pp

@@ -58,6 +58,10 @@ begin
     // (Temporary) indirect dependencies, not detected by fpcmake:
     P.Dependencies.Add('univint', [MacOSX,iphonesim,ios]);
     P.Dependencies.Add('libmicrohttpd', LibMicroHttpdOSes);
+    
+    P.Dependencies.Add('fcl-jsonschema');
+    P.Dependencies.Add('fcl-openapi');
+    
     P.Author := 'FreePascal development team';
     P.License := 'LGPL with modification, ';
     P.HomepageURL := 'www.freepascal.org';
@@ -73,6 +77,7 @@ begin
     P.SourcePath.Add('src/restbridge');
     P.SourcePath.Add('src/websocket');
     P.SourcePath.Add('src/fcm');
+    P.SourcePath.Add('src/openapi');
     T:=P.Targets.addUnit('fpmimetypes.pp');
 
     T:=P.Targets.AddUnit('httpdefs.pp');
@@ -581,7 +586,14 @@ begin
         lOSes := lOSes - [java,android];
       AddUnit('custmicrohttpapp',lOSes);
       end;
-        
+
+    T:=P.Targets.AddUnit('fpopenapiclient.pp');
+    T.Dependencies.AddUnit('fpwebclient');
+
+    T:=P.Targets.AddUnit('fpopenapimodule.pp');
+    T.Dependencies.AddUnit('httpprotocol');
+    T.Dependencies.AddUnit('httpdefs');
+    T.Dependencies.AddUnit('httproute');
 end;
     
 {$ifndef ALLPACKAGES}

+ 3 - 0
packages/fcl-web/namespaced/FpWeb.OpenAPI.Client.pp

@@ -0,0 +1,3 @@
+unit FpWeb.OpenAPI.Client;
+{$DEFINE FPC_DOTTEDUNITS}
+{$i fpopenapiclient.pp}

+ 3 - 0
packages/fcl-web/namespaced/FpWeb.OpenAPI.Module.pp

@@ -0,0 +1,3 @@
+unit FpWeb.OpenAPI.Module;
+{$DEFINE FPC_DOTTEDUNITS}
+{$i fpopenapimodule.pp}

+ 2 - 0
packages/fcl-web/namespaces.lst

@@ -104,3 +104,5 @@ src/jwt/fpoauth2.pp=namespaced/Jwt.Oauth2.pp
 src/fcm/fpfcmclient.pp=Fcm.Client.pp
 src/fcm/fpfcmstrings.pp=Fcm.Strings.pp
 src/fcm/fpfcmtypes.pp=Fcm.Types.pp
+src/openapi/fpopenapiclient.pp=FpWeb.OpenAPI.Client.pp
+src/openapi/fpopenapimodule.pp=FpWeb.OpenAPI.Module.pp

+ 350 - 0
packages/fcl-web/src/openapi/fpopenapiclient.pp

@@ -0,0 +1,350 @@
+{
+    This file is part of the Free Component Library
+    Copyright (c) 2024 by Michael Van Canneyt [email protected]
+
+    Open API service - client proxy parent
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$IFNDEF FPC_DOTTEDUNITS}
+unit fpopenapiclient;
+{$ENDIF FPC_DOTTEDUNITS}
+
+{$mode ObjFPC}
+{$H+}
+{$IFNDEF VER3_2}
+{$modeswitch functionreferences}
+{$modeswitch anonymousfunctions}
+{$ENDIF}
+{$modeswitch advancedrecords}
+
+interface
+
+uses
+  {$IFDEF FPC_DOTTEDUNITS}
+  System.Classes, System.SysUtils, FpWeb.Client;
+  {$ELSE}
+  Classes, SysUtils, fpwebclient;
+  {$ENDIF}
+
+Type
+  EOpenAPIClient = class(Exception);
+  TServiceRequestID = string;
+
+  TServiceResponse = Record
+    RequestID : TServiceRequestID;
+    StatusCode : Integer;
+    StatusText : String;
+    Content : String;
+  end;
+
+  { TServiceResult }
+
+  generic TServiceResult<T> = record
+    RequestID : TServiceRequestID;
+    ErrorCode : Integer;
+    ErrorText : String;
+    Value : T;
+    constructor create(aServiceResponse : TServiceResponse);
+    function Success : Boolean;
+  end;
+
+  TVoidServiceResult = specialize TServiceResult<Boolean>;
+
+  {$IFNDEF VER3_2}
+  TServiceResponseCallback = reference to procedure (aResult: TServiceResponse);
+  TVoidServiceResultCallBack = reference to procedure (aResult: TVoidServiceResult);
+  {$ELSE}
+  TServiceResponseCallback = procedure (aResult: TServiceResponse) of object;
+  TVoidServiceResultCallBack = procedure (aResult: TVoidServiceResult) of object;
+  {$ENDIF}
+
+  { TFPOpenAPIServiceClient }
+  TAPIServicePrepareRequestEvent = procedure(aSender : TObject; aRequest : TWebClientRequest) of object;
+  TAPIServiceProcessResponseEvent = procedure(aSender : TObject; aResponse : TWebClientResponse) of object;
+
+  TFPOpenAPIServiceClient = Class(TComponent)
+  private
+    FBaseURL: String;
+    FOnPrepareRequest: TAPIServicePrepareRequestEvent;
+    FOnProcessResponse: TAPIServiceProcessResponseEvent;
+    FWebClient: TAbstractWebClient;
+    procedure SetBaseURL(AValue: String);
+    procedure SetWebClient(AValue: TAbstractWebClient);
+  protected
+    procedure PrepareRequest(aRequest: TWebClientRequest); virtual;
+    procedure ProcessResponse(aResponse: TWebClientResponse); virtual;
+    procedure ProcessServiceException(aReq : TWebClientRequest; aError : Exception);
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+    function BuildEndPointURL(const aEndPoint : string) : string; virtual;
+    function ReplacePathParam (const aPath : String; const aParamName : string; const aParamValue : String) : String; virtual;
+    function ConcatRestParam(const aQueryParam: string; const aParamName: string; const aParamValue: string): string; virtual;
+    function ExecuteRequest(const aMethod,aURL,aBody : String; aRequestID : TServiceRequestID = '') : TServiceResponse; virtual;
+    {$IFNDEF VER3_2}
+    function ExecuteRequest(const aMethod,aURL,aBody : String; aCallback : TServiceResponseCallback; aRequestID : TServiceRequestID = '') : TServiceRequestID;virtual;
+    {$ENDIF}
+  Published
+    Property WebClient : TAbstractWebClient Read FWebClient Write SetWebClient;
+    Property BaseURL : String Read FBaseURL Write SetBaseURL;
+    Property OnPrepareRequest : TAPIServicePrepareRequestEvent Read FOnPrepareRequest Write FOnPrepareRequest;
+    Property OnProcessResponse : TAPIServiceProcessResponseEvent Read FOnProcessResponse Write FOnProcessResponse;
+  end;
+
+const
+  cRestBooleans : Array[Boolean] of string = ('false','true');
+
+function cRestFormatSettings: TFormatSettings;
+
+implementation
+
+{$IFDEF FPC_DOTTEDUNITS}
+uses FpWeb.Http.Protocol;
+{$ELSE}
+uses httpprotocol;
+{$ENDIF}
+
+var
+  _FormatSettings : TFormatSettings;
+
+function cRestFormatSettings: TFormatSettings;
+
+begin
+  if _FormatSettings.DateSeparator=#0 then
+    _FormatSettings:=DefaultFormatSettings;
+  Result:=_FormatSettings;
+end;
+
+{ TServiceResult }
+
+constructor TServiceResult.create(aServiceResponse: TServiceResponse);
+
+begin
+  Value:=Default(T);
+  if (aServiceResponse.StatusCode div 100)<>2 then
+    begin
+    ErrorCode:=aServiceResponse.StatusCode;
+    ErrorText:=aServiceResponse.StatusText;
+    end
+  else
+    begin
+    ErrorCode:=0;
+    ErrorText:='';
+    end;
+end;
+
+function TServiceResult.Success: Boolean;
+
+begin
+  Result:=ErrorCode=0;
+end;
+
+{ TFPOpenAPIClient }
+
+procedure TFPOpenAPIServiceClient.SetBaseURL(AValue: String);
+
+begin
+  if FBaseURL=AValue then Exit;
+  FBaseURL:=AValue;
+end;
+
+
+procedure TFPOpenAPIServiceClient.SetWebClient(AValue: TAbstractWebClient);
+
+begin
+  if FWebClient=AValue then Exit;
+  if Assigned(FWebClient) then
+    FWebClient.RemoveFreeNotification(Self);
+  FWebClient:=AValue;
+  if Assigned(FWebClient) then
+    FWebClient.FreeNotification(Self);
+end;
+
+
+procedure TFPOpenAPIServiceClient.PrepareRequest(aRequest: TWebClientRequest);
+
+begin
+  aRequest.Headers.Values['Content-Type']:='application/json';
+  aRequest.Headers.Values['Accept']:='application/json';
+  if assigned(OnPrepareRequest) then
+    OnPrepareRequest(Self,aRequest);
+end;
+
+
+procedure TFPOpenAPIServiceClient.ProcessResponse(aResponse: TWebClientResponse);
+
+begin
+  if Assigned(FOnProcessResponse) then
+    FOnProcessResponse(Self,aResponse);
+end;
+
+
+procedure TFPOpenAPIServiceClient.ProcessServiceException(aReq: TWebClientRequest; aError: Exception);
+
+begin
+  // Do nothing
+end;
+
+
+procedure TFPOpenAPIServiceClient.Notification(AComponent: TComponent; Operation: TOperation);
+
+begin
+  inherited Notification(AComponent, Operation);
+  if (Operation=opRemove) then
+    if aComponent=FWebClient then
+      FWebClient:=Nil;
+end;
+
+
+function TFPOpenAPIServiceClient.BuildEndPointURL(const aEndPoint: string): string;
+
+var
+  lEndPoint : String;
+
+begin
+  Result:=BaseURL;
+  if (Result<>'') and (Result[Length(Result)]<>'/') then
+    Result:=Result+'/';
+  lEndPoint:=aEndPoint;
+  if (aEndPoint<>'') and (aEndPoint[1]='/') then
+    lEndPoint:=Copy(lEndPoint,2);
+  Result:=Result+lEndPoint;
+end;
+
+
+function TFPOpenAPIServiceClient.ReplacePathParam(const aPath: String; const aParamName: string; const aParamValue: String): String;
+
+var
+  lEncoded : String;
+
+begin
+  lEncoded:=HTTPEncode(aParamValue);
+  Result:=StringReplace(aPath,'{'+aParamName+'}',lEncoded,[rfReplaceAll]);
+end;
+
+
+function TFPOpenAPIServiceClient.ConcatRestParam(const aQueryParam: string; const aParamName: string; const aParamValue: string): string;
+
+begin
+  Result := aQueryParam;
+  if (aParamValue = '') then
+    exit;
+  if Result='' then
+    Result:=Result+'?'
+  else
+    Result:=Result+'&';
+  Result:=Result+aParamName;
+  Result:=Result+'='+HTTPEncode(aParamValue);
+end;
+
+
+{$IFNDEF VER3_2}
+function TFPOpenAPIServiceClient.ExecuteRequest(const aMethod,aURL,aBody : String; aCallback : TServiceResponseCallback; aRequestID : TServiceRequestID = '') : TServiceRequestID;
+
+var
+  lReq : TWebClientRequest;
+  lResponse : TWebClientResponse;
+  lExResponse : TServiceResponse;
+
+begin
+  Result:=aRequestID;
+  if Not Assigned(WebClient) then
+    Raise EOpenAPIClient.Create('No webclient assigned');
+  try
+    lReq:=WebClient.CreateRequest(True,aRequestID);
+    Result:=lReq.RequestID;
+    if aBody<>'' then
+      lReq.SetContentFromString(aBody);
+    try
+      PrepareRequest(lReq);
+      WebClient.ExecuteRequest(aMethod,aURL,lReq,procedure(aResponse : TWebClientResponseResult)
+         var
+           aResult : TServiceResponse;
+         begin
+           if not aResponse.Success then
+             begin
+             ProcessServiceException(lReq,aResponse.Error);
+             With aResponse.Error do
+               begin
+               aResult.StatusText:=Format('%s : %s',[ClassName,Message]);
+               aResult.StatusCode:=999;
+               aResult.Content:='';
+               end
+             end
+           else
+             begin
+             ProcessResponse(aResponse.Response);
+             aResult.StatusCode:=aResponse.Response.StatusCode;
+             aResult.StatusText:=aResponse.Response.StatusText;
+             aResult.Content:=aResponse.Response.GetContentAsString;
+             end;
+           aCallBack(aResult);
+         end);
+      lReq:=Nil;
+      lResponse:=Nil;
+    except
+      on E : Exception do
+        begin
+        ProcessServiceException(lReq,E);
+        lExResponse.RequestID:=lReq.RequestID;
+        lExResponse.StatusCode:=999;
+        lExResponse.StatusText:=Format('%s: %s',[E.ClassName,E.Message]);
+        aCallBack(lExResponse);
+        end;
+    end;
+  finally
+    lReq.Free;
+    lResponse.Free;
+  end;
+end;
+{$ENDIF}
+
+function TFPOpenAPIServiceClient.ExecuteRequest(const aMethod, aURL, aBody: String; aRequestID: TServiceRequestID): TServiceResponse;
+
+var
+  lReq : TWebClientRequest;
+  lResponse : TWebClientResponse;
+
+begin
+  Result:=Default(TServiceResponse);
+  if Not Assigned(WebClient) then
+    Raise EOpenAPIClient.Create('No webclient assigned');
+  try
+    {$IFNDEF VER3_2}
+    lReq:=WebClient.CreateRequest(False,aRequestID);
+    Result.RequestID:=lReq.RequestID;
+    {$ELSE}
+    Result.RequestID:=aRequestID;
+    lReq:=WebClient.CreateRequest;
+    {$ENDIF}
+    if aBody<>'' then
+      lReq.SetContentFromString(aBody);
+    try
+      PrepareRequest(lReq);
+      lResponse:=WebClient.ExecuteRequest(aMethod,aURL,lReq);
+      ProcessResponse(lResponse);
+      Result.StatusCode:=lResponse.StatusCode;
+      Result.StatusText:=lResponse.StatusText;
+      Result.Content:=lResponse.GetContentAsString;
+    except
+      on E : Exception do
+        begin
+        ProcessServiceException(lReq,E);
+        Result.StatusCode:=999;
+        Result.StatusText:=Format('%s: %s',[E.ClassName,E.Message]);
+        end;
+    end;
+  finally
+    lReq.Free;
+    lResponse.Free;
+  end;
+end;
+
+
+end.
+

+ 263 - 0
packages/fcl-web/src/openapi/fpopenapimodule.pp

@@ -0,0 +1,263 @@
+{
+    This file is part of the Free Component Library
+    Copyright (c) 2024 by Michael Van Canneyt [email protected]
+
+    Open API service - web server module
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$IFNDEF FPC_DOTTEDUNITS}
+unit fpopenapimodule;
+{$ENDIF}
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+
+{$IFDEF FPC_DOTTEDUNITS}
+uses System.SysUtils, System.Classes, System.Contnrs, FpJson.Data, FpWeb.Http.Protocol, FpWeb.Http.Defs, FpWeb.Route;
+{$ELSE}
+uses sysutils, classes, contnrs, fpjson, httpprotocol, httpdefs, httproute;
+{$ENDIF}
+
+Type
+  TFPOpenAPIModule = Class;
+  TFPOpenAPIModuleClass = Class of TFPOpenAPIModule;
+
+  TOpenAPIBeforeRequestHandler = Procedure(Sender : TObject; aRequest : TRequest; aResponse: TResponse; var aContinue: Boolean) of object;
+  TOpenAPIAfterRequestHandler = Procedure(Sender : TObject; aRequest : TRequest; aResponse: TResponse) of object;
+  { TFPOpenAPIModule }
+  TArgumentLocation = (alQuery,alPath,alHeader,alCookie);
+  TArgumentLocations = set of TArgumentLocation;
+
+  TFPOpenAPIModule = class(TDataModule)
+  Private
+    FAfterRequest: TOpenAPIAfterRequestHandler;
+    FBeforeRequest: TOpenAPIBeforeRequestHandler;
+    Type
+      TOpenAPIRoute = Class(TObject)
+        APIMethod : Pointer;
+        APIClass : TFPOpenAPIModuleClass;
+        Streaming : Boolean;
+        constructor create(aMethod : TMethod; aStreaming : Boolean);
+      end;
+    class var
+      _Routes : TFPObjectList;
+  protected
+    class constructor Init;
+    class destructor Done;
+    class procedure RegisterOpenAPIRoute(aBaseURL, aEndPoint: String; aMethodPointer: TRouteEvent; aStreaming: Boolean);
+    class function GetClassInstance(aClass: TFPOpenAPIModuleClass; aOwner: TComponent=nil; aStreaming: Boolean=False): TFPOpenAPIModule; virtual;
+    class procedure ReleaseClassInstance(aInstance: TFPOpenAPIModule); virtual;
+    class procedure HandleOpenAPIRequest(aData : Pointer; ARequest: TRequest; AResponse: TResponse); static;
+    function DoPrepareRequest(aRequest: TRequest; aResponse : TResponse) : Boolean;
+    procedure DoProcessResponse(aRequest: TRequest; aResponse : TResponse);
+    function PrepareRequest(aRequest: TRequest; aResponse : TResponse) : Boolean;
+    Procedure ProcessResponse(aRequest: TRequest; aResponse : TResponse);
+    procedure handleRequestError(aError : Exception; aRequest : TRequest; aResponse : TResponse); virtual;
+    function ExtractRequestArgument(aRequest : TRequest; aLocation : TArgumentLocation; aName : String; aDefault : String) : String;
+    function ExtractRequestArgument(aRequest : TRequest; aLocation : TArgumentLocation; aName : String; aDefault : Integer) : Integer;
+    function ExtractRequestArgument(aRequest : TRequest; aLocation : TArgumentLocation; aName : String; aDefault : Int64) : Int64;
+    function ExtractRequestArgument(aRequest : TRequest; aLocation : TArgumentLocation; aName : String; aDefault : TDateTime) : TDateTime;
+    function ExtractRequestArgument(aRequest : TRequest; aLocation : TArgumentLocation; aName : String; aDefault : Double) : Double;
+  Public
+    class var APIModuleOwner : TComponent;
+    class Procedure RegisterAPIRoutes(aBaseURL : String; aUseStreaming : Boolean = False); virtual; abstract;
+  published
+    Property BeforeRequest : TOpenAPIBeforeRequestHandler Read FBeforeRequest Write FBeforeRequest;
+    Property AfterRequest : TOpenAPIAfterRequestHandler Read FAfterRequest Write FAfterRequest;
+  end;
+
+implementation
+
+{$IFDEF FPC_DOTTEDUNITS}
+uses System.DateUtils;
+{$ELSE}
+uses dateutils;
+{$ENDIF}
+
+{ TFPOpenAPIModule.TOpenAPIRoute }
+
+constructor TFPOpenAPIModule.TOpenAPIRoute.create(aMethod: TMethod; aStreaming : Boolean);
+begin
+  APIMethod:=aMethod.Code;
+  // As a class method, Data contains the class pointer...
+  APIClass:=TFPOpenAPIModuleClass(aMethod.Data);
+  Streaming:=aStreaming;
+end;
+
+{ TFPOpenAPIModule }
+
+class constructor TFPOpenAPIModule.Init;
+begin
+  _Routes:=TFPObjectList.Create(True);
+end;
+
+class destructor TFPOpenAPIModule.Done;
+begin
+  FreeAndNil(_Routes);
+end;
+
+class procedure TFPOpenAPIModule.RegisterOpenAPIRoute(aBaseURL, aEndPoint: String; aMethodPointer: TRouteEvent; aStreaming : Boolean);
+
+var
+  lRoute : TOpenAPIRoute;
+  lURL,lEnd : string;
+
+begin
+  lRoute:=TOpenAPIRoute.create(TMethod(aMethodPointer),aStreaming);
+  _Routes.Add(lRoute);
+  lURL:=IncludeHTTPPathDelimiter(aBaseURL);
+  lEnd:=aEndPoint;
+  While (lEnd<>'') and (lEnd[1]='/') do
+    Delete(lEnd,1,1);
+  lURL:=lURL+lEnd;
+  HTTPRouter.RegisterRoute(lURL,lRoute,@HandleOpenAPIRequest,False);
+end;
+
+class function TFPOpenAPIModule.GetClassInstance(aClass: TFPOpenAPIModuleClass; aOwner : TComponent = Nil; aStreaming : Boolean = False): TFPOpenAPIModule;
+begin
+  if aStreaming then
+    Result:=TFPOpenAPIModuleClass(aClass).Create(aOwner)
+  else
+    Result:=TFPOpenAPIModuleClass(aClass).CreateNew(aOwner,0);
+end;
+
+class procedure TFPOpenAPIModule.ReleaseClassInstance(aInstance: TFPOpenAPIModule);
+begin
+  aInstance.Free;
+end;
+
+class procedure TFPOpenAPIModule.HandleOpenAPIRequest(aData: Pointer; ARequest: TRequest; AResponse: TResponse);
+
+var
+  aRoute : TOpenAPIRoute absolute aData;
+
+var
+  lModule : TFPOpenAPIModule;
+  lMethod : TMethod;
+
+begin
+  lModule:=GetClassInstance(aRoute.APIClass,APIModuleOwner,aRoute.Streaming);
+  try
+    lMethod.Data:=lModule;
+    lMethod.Code:=aRoute.APIMethod;
+    TRouteEvent(lMethod)(aRequest,aResponse);
+  finally
+    ReleaseClassInstance(lModule);
+  end;
+end;
+
+function TFPOpenAPIModule.DoPrepareRequest(aRequest: TRequest; aResponse: TResponse): Boolean;
+begin
+  Result:=True;
+  if Assigned(BeforeRequest) then
+    BeforeRequest(Self,aRequest,aResponse,Result);
+end;
+
+procedure TFPOpenAPIModule.DoProcessResponse(aRequest: TRequest; aResponse: TResponse);
+begin
+  if Assigned(AfterRequest) then
+    AfterRequest(Self,aRequest,aResponse);
+end;
+
+function TFPOpenAPIModule.PrepareRequest(aRequest: TRequest; aResponse: TResponse): Boolean;
+begin
+  aResponse.ContentType:='application/json';
+  Result:=DoPrepareRequest(aRequest,aResponse);
+end;
+
+procedure TFPOpenAPIModule.ProcessResponse(aRequest: TRequest; aResponse: TResponse);
+begin
+  if (aResponse.ContentLength=0) then
+    aResponse.ContentLength:=Length(aResponse.Content);
+  DoProcessResponse(aRequest,aResponse);
+  if not aResponse.ContentSent then
+    aResponse.SendContent;
+end;
+
+procedure TFPOpenAPIModule.handleRequestError(aError: Exception; aRequest: TRequest; aResponse: TResponse);
+
+var
+  lError : TJSONObject;
+
+begin
+  lError:=TJSONObject.Create(['classname',aError.ClassName,'message',aError.message]);
+  aResponse.Code:=500;
+  aResponse.CodeText:='INTERNAL SERVER ERROR';
+  aResponse.Content:=lError.AsJSON;
+  aResponse.ContentLength:=Length(aResponse.Content);
+  aResponse.ContentType:='application/json';
+  ProcessResponse(aRequest,aResponse);
+end;
+
+function TFPOpenAPIModule.ExtractRequestArgument(aRequest: TRequest;aLocation : TArgumentLocation; aName: String; aDefault: String): String;
+begin
+  case aLocation of
+    alPath: Result:=aRequest.RouteParams[aName];
+    alQuery : Result:=aRequest.QueryFields.Values[aName];
+    alHeader : Result:=aRequest.GetCustomHeader(aName);
+    alCookie : Result:=aRequest.CookieFields.Values[aName];
+  end;
+  if (Result='') then
+    Result:=aDefault;
+end;
+
+function TFPOpenAPIModule.ExtractRequestArgument(aRequest: TRequest; aLocation : TArgumentLocation; aName: String; aDefault: Integer): Integer;
+
+var
+  S : String;
+
+begin
+  S:=ExtractRequestArgument(aRequest,aLocation,aName,'');
+  Result:=StrToIntDef(S,aDefault);
+end;
+
+function TFPOpenAPIModule.ExtractRequestArgument(aRequest: TRequest; aLocation : TArgumentLocation; aName: String; aDefault: Int64): Int64;
+var
+  S : String;
+
+begin
+  S:=ExtractRequestArgument(aRequest,aLocation,aName,'');
+  Result:=StrToInt64Def(S,aDefault);
+end;
+
+function TFPOpenAPIModule.ExtractRequestArgument(aRequest: TRequest; aLocation : TArgumentLocation; aName: String; aDefault: TDateTime): TDateTime;
+
+var
+  S : String;
+
+begin
+  S:=ExtractRequestArgument(aRequest,aLocation,aName,'');
+  if S='' then
+    Result:=aDefault
+  else if not TryISOStrToDateTime(S,Result) then
+    Result:=aDefault;
+end;
+
+function TFPOpenAPIModule.ExtractRequestArgument(aRequest: TRequest; aLocation : TArgumentLocation; aName: String; aDefault: Double): Double;
+var
+  S : String;
+  aCode : Integer;
+
+begin
+  S:=ExtractRequestArgument(aRequest,aLocation,aName,'');
+  if S='' then
+    Result:=aDefault
+  else
+    begin
+    Val(S,Result,aCode);
+    if aCode<>0 then
+      Result:=aDefault;
+    end;
+end;
+
+end.
+