delphimvcframework/sources/MVCFramework.RESTClient.pas

1053 lines
29 KiB
ObjectPascal
Raw Normal View History

2013-10-30 00:48:23 +01:00
unit MVCFramework.RESTClient;
interface
uses
Classes,
IdBaseComponent,
IdComponent,
IdTCPConnection,
IdTCPClient,
IdHTTP,
idURI,
2014-09-05 12:47:40 +02:00
{$IF CompilerVersion < 27}
2014-04-16 22:52:25 +02:00
Data.DBXJSON,
{$ELSE}
System.JSON,
{$IFEND}
2013-10-30 00:48:23 +01:00
IdMultipartFormData,
2014-04-01 22:32:57 +02:00
System.SysUtils, Data.DB, IdIOHandler;
2013-10-30 00:48:23 +01:00
type
TArrayOfString = array of string;
2014-03-24 17:37:08 +01:00
THttpCommand = (httpGET, httpPOST, httpPUT, httpDELETE, httpPATCH, httpTRACE);
2013-10-30 00:48:23 +01:00
IRESTResponse = interface
function BodyAsString: string;
function BodyAsJsonObject: TJSONObject;
function BodyAsJsonValue: TJSONValue;
2013-10-30 00:48:23 +01:00
function ResponseCode: Word;
function ResponseText: string;
function Headers: TStringlist;
2014-06-27 15:30:39 +02:00
function GetContentType: string;
function GetContentEncoding: string;
2013-10-30 00:48:23 +01:00
function Body: TStringStream;
2014-06-27 15:30:39 +02:00
function GetHeaderValue(const Name: string): string;
2013-10-30 00:48:23 +01:00
procedure SetResponseCode(AResponseCode: Word);
procedure SetResponseText(AResponseText: string);
procedure SetHeaders(AHeaders: TStrings);
end;
TRESTResponse = class(TInterfacedObject, IRESTResponse)
private
2013-11-05 14:57:50 +01:00
FBody: TStringStream;
FResponseCode: Word;
FResponseText: string;
FHeaders: TStringlist;
FBodyAsJSONValue: TJSONValue;
2013-11-05 14:57:50 +01:00
FContentType: string;
2014-06-27 15:30:39 +02:00
FContentEncoding: string;
function GetHeader(const Value: string): string;
2013-10-30 00:48:23 +01:00
public
2013-10-30 00:48:23 +01:00
function BodyAsString: string;
function BodyAsJsonObject: TJSONObject;
function BodyAsJsonValue: TJSONValue;
2013-10-30 00:48:23 +01:00
function ResponseCode: Word;
function ResponseText: string;
function Headers: TStringlist;
function Body: TStringStream;
2014-06-27 15:30:39 +02:00
function GetContentType: string;
function GetContentEncoding: string;
2013-10-30 00:48:23 +01:00
procedure SetResponseCode(AResponseCode: Word);
procedure SetResponseText(AResponseText: string);
procedure SetHeaders(AHeaders: TStrings);
constructor Create; virtual;
destructor Destroy; override;
function GetHeaderValue(const Name: string): string;
2013-10-30 00:48:23 +01:00
end;
TRESTClient = class(TInterfacedObject)
private
2013-11-05 14:57:50 +01:00
FServerName: string;
FServerPort: Word;
FBodyParams: TStringlist;
FQueryStringParams: TStringlist;
FAccept: string;
FRawBody: TStringStream;
FHTTP: TIdHTTP;
FContentType: string;
FLastSessionID: string;
2013-10-30 00:48:23 +01:00
FNextRequestIsAsynch: Boolean;
FAsynchProc: TProc<IRESTResponse>;
FAsynchProcErr: TProc<Exception>;
2013-11-05 14:57:50 +01:00
FPrimaryThread: TThread;
FMultiPartFormData: TIdMultiPartFormDataStream;
FAsynchProcAlways: TProc;
2014-04-01 22:32:57 +02:00
FProtocol: string;
2014-04-02 20:27:17 +02:00
FSynchronized: Boolean;
2014-06-27 15:30:39 +02:00
FContentEncoding: string;
2013-10-30 00:48:23 +01:00
function EncodeQueryStringParams(const AQueryStringParams: TStrings;
IncludeQuestionMark: Boolean = true): string;
procedure SetBodyParams(const Value: TStringlist);
procedure SetQueryStringParams(const Value: TStringlist);
procedure SetAccept(const Value: string);
procedure SetContentType(const Value: string);
function GetSessionID: string;
procedure SetSessionID(const Value: string);
function GetBodyParams: TStringlist;
function GetQueryStringParams: TStringlist;
function GetRawBody: TStringStream;
procedure SetReadTimeout(const Value: Integer);
function GetReadTimeout: Integer;
procedure StartAsynchRequest(AHTTPMethod: THttpCommand; AUrl: string;
2014-06-27 15:30:39 +02:00
ABodyString: string); overload;
procedure StartAsynchRequest(AHTTPMethod: THttpCommand;
AUrl: string); overload;
2013-10-30 00:48:23 +01:00
procedure SetConnectionTimeout(const Value: Integer);
function GetConnectionTimeout: Integer;
procedure SetRequestHeaders(const Value: TStringlist);
strict protected
FRequestHeaders: TStringlist;
protected
procedure HandleCookies;
function EncodeResourceParams(AResourceParams: array of string): string;
function HttpCommandToString(const AHttpCommand: THttpCommand): string;
function SendHTTPCommand(const ACommand: THttpCommand;
const AAccept, AContentType, AUrl: string; ABodyParams: TStrings)
: IRESTResponse;
function SendHTTPCommandWithBody(const ACommand: THttpCommand;
2014-06-27 15:30:39 +02:00
const AAccept, AContentType, AUrl: string; ABodyString: string)
2013-10-30 00:48:23 +01:00
: IRESTResponse;
procedure HandleRequestCookies;
function GetMultipartFormData: TIdMultiPartFormDataStream;
public
constructor Create(const AServerName: string;
2014-04-01 22:32:57 +02:00
AServerPort: Word = 80; AIOHandler: TIdIOHandler = nil); virtual;
2013-10-30 00:48:23 +01:00
destructor Destroy; override;
function AddFile(const FieldName, FileName: string;
const ContentType: string = ''): TRESTClient;
2013-10-30 00:48:23 +01:00
function Asynch(AProc: TProc<IRESTResponse>;
2014-04-02 20:27:17 +02:00
AProcErr: TProc<Exception> = nil; AProcAlways: TProc = nil; ASynchronized: Boolean = false): TRESTClient;
2013-10-30 00:48:23 +01:00
function ClearAllParams: TRESTClient;
function ResetSession: TRESTClient;
function Accept(const AcceptHeader: string): TRESTClient; overload;
function Accept: string; overload;
function ContentType(const ContentTypeHeader: string): TRESTClient;
overload;
function ContentType: string; overload;
2014-04-10 13:56:23 +02:00
function ContentEncoding(const ContentEncodingHeader: string): TRESTClient;
overload;
function ContentEncoding: string; overload;
2013-10-30 00:48:23 +01:00
// requests
function doGET(AResource: string; AResourceParams: array of string)
: IRESTResponse;
function doPOST(AResource: string; AResourceParams: array of string)
: IRESTResponse; overload;
function doPOST(AResource: string; AResourceParams: array of string;
AJSONValue: TJSONValue; AOwnsJSONBody: Boolean = true)
: IRESTResponse; overload;
function doPOST(AResource: string; AResourceParams: array of string;
2014-06-27 15:30:39 +02:00
ABodyString: string): IRESTResponse; overload;
2014-03-24 17:37:08 +01:00
function doPATCH(AResource: string; AResourceParams: array of string;
AJSONValue: TJSONValue; AOwnsJSONBody: Boolean = true)
: IRESTResponse; overload;
function doPATCH(AResource: string; AResourceParams: array of string;
2014-06-27 15:30:39 +02:00
ABodyString: string): IRESTResponse; overload;
2013-10-30 00:48:23 +01:00
function doPUT(AResource: string; AResourceParams: array of string)
: IRESTResponse; overload;
function doPUT(AResource: string; AResourceParams: array of string;
AJSONValue: TJSONValue; AOwnsJSONBody: Boolean = true)
: IRESTResponse; overload;
2013-11-11 12:23:49 +01:00
function doPUT(AResource: string; AResourceParams: array of string;
2014-06-27 15:30:39 +02:00
ABodyString: string): IRESTResponse; overload;
2013-10-30 00:48:23 +01:00
function doDELETE(AResource: string; AResourceParams: array of string)
: IRESTResponse;
property BodyParams: TStringlist read GetBodyParams write SetBodyParams;
property RawBody: TStringStream read GetRawBody;
property QueryStringParams: TStringlist read GetQueryStringParams
write SetQueryStringParams;
property SessionID: string read GetSessionID write SetSessionID;
property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout;
property ConnectionTimeout: Integer read GetConnectionTimeout
write SetConnectionTimeout;
property RequestHeaders: TStringlist read FRequestHeaders
write SetRequestHeaders;
// dataset specific methods
2014-06-27 15:30:39 +02:00
function DSUpdate(const URL: string; DataSet: TDataSet; const KeyValue: string): IRESTResponse;
function DSInsert(const URL: string; DataSet: TDataSet): IRESTResponse;
function DSDelete(const URL: string; const KeyValue: string): IRESTResponse;
2013-10-30 00:48:23 +01:00
end;
function StringsToArrayOfString(const AStrings: TStrings): TArrayOfString;
implementation
uses
ObjectsMappers;
2013-10-30 00:48:23 +01:00
{ TRSF }
function StringsToArrayOfString(const AStrings: TStrings): TArrayOfString;
var
i: Integer;
begin
SetLength(Result, AStrings.Count);
if AStrings.Count = 0 then
Exit;
for i := 0 to AStrings.Count - 1 do
Result[i] := AStrings[i];
end;
function TRESTClient.Accept(const AcceptHeader: string): TRESTClient;
begin
SetAccept(AcceptHeader);
Result := Self;
end;
function TRESTClient.Accept: string;
begin
Result := FAccept;
end;
function TRESTClient.AddFile(const FieldName, FileName, ContentType: string)
: TRESTClient;
2013-10-30 00:48:23 +01:00
begin
GetMultipartFormData.AddFile(FieldName, FileName, ContentType);
2014-06-27 15:30:39 +02:00
Result := Self;
2013-10-30 00:48:23 +01:00
end;
function TRESTClient.Asynch(AProc: TProc<IRESTResponse>;
2014-04-02 20:27:17 +02:00
AProcErr: TProc<Exception>; AProcAlways: TProc; ASynchronized: Boolean): TRESTClient;
2013-10-30 00:48:23 +01:00
begin
FNextRequestIsAsynch := true;
FAsynchProc := AProc;
FAsynchProcErr := AProcErr;
FAsynchProcAlways := AProcAlways;
2014-04-02 20:27:17 +02:00
FSynchronized := ASynchronized;
2013-10-30 00:48:23 +01:00
Result := Self;
end;
function TRESTClient.ClearAllParams: TRESTClient;
begin
if Assigned(FRawBody) then
begin
FRawBody.Size := 0;
FRawBody.Position := 0;
end;
if Assigned(FBodyParams) then
FBodyParams.Clear;
if Assigned(FQueryStringParams) then
FQueryStringParams.Clear;
Result := Self;
2014-04-02 20:27:17 +02:00
FNextRequestIsAsynch := false;
FAsynchProc := nil;
FAsynchProcErr := nil;
FAsynchProcAlways := nil;
2013-10-30 00:48:23 +01:00
end;
2014-04-10 13:56:23 +02:00
function TRESTClient.ContentEncoding(const ContentEncodingHeader: string): TRESTClient;
begin
FContentEncoding := ContentEncodingHeader;
Result := Self;
end;
function TRESTClient.ContentEncoding: string;
begin
Result := FContentEncoding;
end;
2013-10-30 00:48:23 +01:00
function TRESTClient.ContentType: string;
begin
Result := FContentType;
end;
function TRESTClient.ContentType(const ContentTypeHeader: string): TRESTClient;
begin
SetContentType(ContentTypeHeader);
Result := Self;
end;
2014-04-01 22:32:57 +02:00
constructor TRESTClient.Create(const AServerName: string; AServerPort: Word; AIOHandler: TIdIOHandler);
var
2014-06-27 15:30:39 +02:00
Pieces: TArray<string>;
2013-10-30 00:48:23 +01:00
begin
inherited Create;
FPrimaryThread := TThread.CurrentThread;
FServerName := AServerName;
FServerPort := AServerPort;
FBodyParams := nil; // TStringlist.Create;
FQueryStringParams := nil; // TStringlist.Create;
FRawBody := nil; // TStringStream.Create('');
FAccept := 'application/json';
FContentType := 'application/json; charset=utf-8';
FRequestHeaders := TStringlist.Create;
2014-04-01 22:32:57 +02:00
if AServerName.Contains('://') then
begin
Pieces := FServerName.Split(['://'], 2, TStringSplitOptions.ExcludeEmpty);
FProtocol := Pieces[0];
FServerName := Pieces[1];
end
else
FProtocol := 'http';
2013-10-30 00:48:23 +01:00
FHTTP := TIdHTTP.Create(nil);
FHTTP.ReadTimeout := 20000;
2014-04-01 22:32:57 +02:00
FHTTP.IOHandler := AIOHandler;
2013-10-30 00:48:23 +01:00
// FHTTP.AllowCookies := true;
end;
destructor TRESTClient.Destroy;
begin
FreeAndNil(FBodyParams);
FreeAndNil(FQueryStringParams);
FreeAndNil(FRawBody);
FreeAndNil(FRequestHeaders);
FreeAndNil(FMultiPartFormData);
FHTTP.Free;
inherited;
end;
function TRESTClient.doDELETE(AResource: string;
AResourceParams: array of string): IRESTResponse;
var
URL: string;
2013-10-30 00:48:23 +01:00
begin
2014-04-01 22:32:57 +02:00
URL := FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2013-10-30 00:48:23 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams);
if FNextRequestIsAsynch then
begin
Result := nil;
StartAsynchRequest(httpDELETE, URL);
2013-10-30 00:48:23 +01:00
end
else
begin
Result := SendHTTPCommand(httpDELETE, FAccept, FContentType, URL, nil);
2013-10-30 00:48:23 +01:00
ClearAllParams;
end;
end;
procedure TRESTClient.StartAsynchRequest(AHTTPMethod: THttpCommand;
AUrl: string);
begin
StartAsynchRequest(AHTTPMethod, AUrl, '');
2013-10-30 00:48:23 +01:00
end;
procedure TRESTClient.StartAsynchRequest(AHTTPMethod: THttpCommand;
2014-06-27 15:30:39 +02:00
AUrl: string; ABodyString: string);
2013-10-30 00:48:23 +01:00
var
th: TThread;
begin
th := TThread.CreateAnonymousThread(
procedure
var
R: IRESTResponse;
begin
try
R := SendHTTPCommandWithBody(AHTTPMethod, FAccept, FContentType, AUrl,
ABodyString);
2013-10-30 00:48:23 +01:00
TMonitor.Enter(TObject(R));
try
2014-04-02 20:27:17 +02:00
if FSynchronized then
TThread.Synchronize(nil,
procedure
begin
FAsynchProc(R);
end)
else
FAsynchProc(R);
2013-10-30 00:48:23 +01:00
finally
TMonitor.Exit(TObject(R));
end;
except
on E: Exception do
begin
2014-04-02 20:27:17 +02:00
if FSynchronized then
TThread.Synchronize(nil,
procedure
begin
FAsynchProcErr(E);
end)
else
FAsynchProcErr(E);
2013-10-30 00:48:23 +01:00
end;
end;
if Assigned(FAsynchProcAlways) then
2014-04-02 20:27:17 +02:00
begin
if FSynchronized then
TThread.Synchronize(nil,
procedure
begin
FAsynchProcAlways();
end)
else
FAsynchProcAlways();
end;
2014-06-27 15:30:39 +02:00
ClearAllParams;
2013-10-30 00:48:23 +01:00
end);
th.Start;
end;
2014-04-02 20:27:17 +02:00
function TRESTClient.doGET(AResource: string; AResourceParams: array of string): IRESTResponse;
2013-10-30 00:48:23 +01:00
var
URL: string;
2013-10-30 00:48:23 +01:00
begin
2014-04-01 22:32:57 +02:00
URL := FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2013-10-30 00:48:23 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams);
if FNextRequestIsAsynch then
begin
Result := nil;
StartAsynchRequest(httpGET, URL);
2013-10-30 00:48:23 +01:00
end
else
begin
Result := SendHTTPCommand(httpGET, FAccept, FContentType, URL, nil);
2013-10-30 00:48:23 +01:00
ClearAllParams;
end;
end;
2014-04-02 20:27:17 +02:00
function TRESTClient.doPOST(AResource: string; AResourceParams: array of string): IRESTResponse;
2013-10-30 00:48:23 +01:00
var
s: string;
begin
try
Result := SendHTTPCommand(httpPOST, FAccept, FContentType,
2014-04-01 22:32:57 +02:00
FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2013-10-30 00:48:23 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams), FBodyParams);
except
on E: EIdHTTPProtocolException do
begin
s := E.Message;
end;
end;
ClearAllParams;
end;
function TRESTClient.doPOST(AResource: string; AResourceParams: array of string;
2013-11-05 14:57:50 +01:00
AJSONValue: TJSONValue; AOwnsJSONBody: Boolean): IRESTResponse;
begin
2013-11-11 12:23:49 +01:00
if not Assigned(AJSONValue) then
raise Exception.Create('AJSONValue is nil');
try
Result := doPOST(AResource, AResourceParams,
{$IF CompilerVersion >= 28}
AJSONValue.ToJSON
{$ELSE}
AJSONValue.ToString
{$ENDIF});
finally
if AOwnsJSONBody then
FreeAndNil(AJSONValue);
end;
end;
2014-06-27 15:30:39 +02:00
function TRESTClient.doPATCH(AResource: string; AResourceParams: array of string; ABodyString: string): IRESTResponse;
2014-03-24 17:37:08 +01:00
var
URL: string;
2014-03-24 17:37:08 +01:00
begin
2014-04-01 22:32:57 +02:00
URL := FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2014-03-24 17:37:08 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams);
if FNextRequestIsAsynch then
begin
Result := nil;
StartAsynchRequest(httpPOST, URL, ABodyString);
2014-03-24 17:37:08 +01:00
end
else
begin
Result := SendHTTPCommandWithBody(httpPATCH, FAccept, FContentType, URL,
2014-03-24 17:37:08 +01:00
ABodyString);
ClearAllParams;
end;
end;
function TRESTClient.doPATCH(AResource: string; AResourceParams: array of string; AJSONValue: TJSONValue;
AOwnsJSONBody: Boolean): IRESTResponse;
begin
if not Assigned(AJSONValue) then
raise Exception.Create('AJSONValue is nil');
try
Result := doPATCH(AResource, AResourceParams,
{$IF CompilerVersion >= 28}
AJSONValue.ToJSON
{$ELSE}
AJSONValue.ToString
{$ENDIF});
2014-03-24 17:37:08 +01:00
finally
if AOwnsJSONBody then
FreeAndNil(AJSONValue);
end;
end;
function TRESTClient.doPOST(AResource: string; AResourceParams: array of string;
2014-06-27 15:30:39 +02:00
ABodyString: string): IRESTResponse;
2013-10-30 00:48:23 +01:00
var
URL: string;
2013-10-30 00:48:23 +01:00
begin
2014-04-01 22:32:57 +02:00
URL := FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2013-10-30 00:48:23 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams);
if FNextRequestIsAsynch then
begin
Result := nil;
StartAsynchRequest(httpPOST, URL, ABodyString);
2013-10-30 00:48:23 +01:00
end
else
begin
Result := SendHTTPCommandWithBody(httpPOST, FAccept, FContentType, URL,
ABodyString);
ClearAllParams;
2013-10-30 00:48:23 +01:00
end;
end;
function TRESTClient.doPUT(AResource: string; AResourceParams: array of string;
2014-06-27 15:30:39 +02:00
ABodyString: string): IRESTResponse;
2013-10-30 00:48:23 +01:00
var
URL: string;
2013-10-30 00:48:23 +01:00
begin
2014-04-01 22:32:57 +02:00
URL := FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2013-10-30 00:48:23 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams);
if FNextRequestIsAsynch then
begin
Result := nil;
StartAsynchRequest(httpPUT, URL, ABodyString);
2013-10-30 00:48:23 +01:00
end
else
begin
Result := SendHTTPCommandWithBody(httpPUT, FAccept, FContentType, URL,
2013-11-11 12:23:49 +01:00
ABodyString);
ClearAllParams;
end;
end;
2014-06-27 15:30:39 +02:00
function TRESTClient.DSDelete(const URL, KeyValue: string): IRESTResponse;
begin
Result := doDELETE(URL, [KeyValue]);
end;
2014-06-27 15:30:39 +02:00
function TRESTClient.DSInsert(const URL: string; DataSet: TDataSet): IRESTResponse;
begin
Result := doPOST(URL, [], DataSet.AsJSONObjectString);
end;
2014-06-27 15:30:39 +02:00
function TRESTClient.DSUpdate(const URL: string; DataSet: TDataSet; const KeyValue: string): IRESTResponse;
begin
Result := doPUT(URL, [KeyValue], DataSet.AsJSONObjectString);
end;
2013-11-11 12:23:49 +01:00
function TRESTClient.doPUT(AResource: string; AResourceParams: array of string;
AJSONValue: TJSONValue; AOwnsJSONBody: Boolean = true): IRESTResponse;
begin
if not Assigned(AJSONValue) then
raise Exception.Create('AJSONValue is nil');
try
Result := doPUT(AResource, AResourceParams,
{$IF CompilerVersion >= 28}
AJSONValue.ToJSON
{$ELSE}
AJSONValue.ToString
{$ENDIF});
2013-11-11 12:23:49 +01:00
finally
if AOwnsJSONBody then
FreeAndNil(AJSONValue);
2013-10-30 00:48:23 +01:00
end;
end;
function TRESTClient.doPUT(AResource: string; AResourceParams: array of string)
: IRESTResponse;
begin
Result := SendHTTPCommand(httpPUT, FAccept, FContentType,
2014-04-01 22:32:57 +02:00
FProtocol + '://' + FServerName + ':' + inttostr(FServerPort) + AResource +
2013-10-30 00:48:23 +01:00
EncodeResourceParams(AResourceParams) + EncodeQueryStringParams
(FQueryStringParams), FBodyParams);
ClearAllParams;
end;
function TRESTClient.EncodeResourceParams(AResourceParams
: array of string): string;
var
i: Integer;
begin
Result := '';
for i := low(AResourceParams) to high(AResourceParams) do
Result := Result + '/' + TIdURI.ParamsEncode(AResourceParams[i]);
end;
function TRESTClient.GetBodyParams: TStringlist;
begin
if not Assigned(FBodyParams) then
FBodyParams := TStringlist.Create;
Result := FBodyParams;
end;
function TRESTClient.GetConnectionTimeout: Integer;
begin
Result := FHTTP.ConnectTimeout;
end;
function TRESTClient.GetMultipartFormData: TIdMultiPartFormDataStream;
begin
if not Assigned(FMultiPartFormData) then
FMultiPartFormData := TIdMultiPartFormDataStream.Create;
Result := FMultiPartFormData;
end;
function TRESTClient.GetQueryStringParams: TStringlist;
begin
if not Assigned(FQueryStringParams) then
FQueryStringParams := TStringlist.Create;
Result := FQueryStringParams;
end;
function TRESTClient.GetRawBody: TStringStream;
begin
if not Assigned(FRawBody) then
FRawBody := TStringStream.Create('');
Result := FRawBody;
end;
function TRESTClient.GetSessionID: string;
begin
Result := Self.FLastSessionID;
end;
function TRESTClient.GetReadTimeout: Integer;
begin
Result := FHTTP.ReadTimeout;
end;
function TRESTClient.EncodeQueryStringParams(const AQueryStringParams: TStrings;
2013-11-05 14:57:50 +01:00
IncludeQuestionMark: Boolean = true): string;
2013-10-30 00:48:23 +01:00
var
i: Integer;
begin
Result := '';
if not Assigned(AQueryStringParams) or (AQueryStringParams.Count = 0) then
Exit;
if IncludeQuestionMark then
Result := '?';
for i := 0 to AQueryStringParams.Count - 1 do
begin
if i > 0 then
Result := Result + '&';
Result := Result + AQueryStringParams.Names[i] + '=' +
TIdURI.ParamsEncode(AQueryStringParams.ValueFromIndex[i]);
end;
end;
procedure TRESTClient.HandleCookies;
var
2013-11-05 14:57:50 +01:00
s: string;
2013-10-30 00:48:23 +01:00
arr: TArray<string>;
begin
// Log('Received cookies', FHTTP.Response.RawHeaders.Text);
for s in FHTTP.Response.RawHeaders do
begin
if s.StartsWith('Set-Cookie', true) then
begin
arr := s.Split([':'], 2);
if arr[1].trim.StartsWith('dtsessionid') then
begin
arr := arr[1].Split(['='], 2);
FLastSessionID := TIdURI.URLDecode(arr[1].Split([';'])[0]);
end;
Break;
end;
end;
end;
procedure TRESTClient.HandleRequestCookies;
var
i: Integer;
begin
if Assigned(FHTTP.CookieManager) then
FHTTP.CookieManager.CookieCollection.Clear;
if not FLastSessionID.trim.IsEmpty then
FHTTP.Request.CustomHeaders.AddValue('Cookie',
'dtsessionid=' + FLastSessionID);
for i := 0 to FRequestHeaders.Count - 1 do
begin
FHTTP.Request.CustomHeaders.AddValue(FRequestHeaders.Names[i],
FRequestHeaders.ValueFromIndex[i]);
end;
end;
function TRESTClient.HttpCommandToString(const AHttpCommand
: THttpCommand): string;
begin
case AHttpCommand of
httpGET:
Result := 'GET';
httpPOST:
Result := 'POST';
httpPUT:
Result := 'PUT';
httpDELETE:
Result := 'DELETE';
2013-11-05 14:57:50 +01:00
else
raise Exception.Create('Unknown HttpCommand in TRSF.HttpCommandToString');
2013-10-30 00:48:23 +01:00
end;
end;
function TRESTClient.ResetSession: TRESTClient;
begin
SessionID := '';
if Assigned(FHTTP.CookieManager) then
FHTTP.CookieManager.CookieCollection.Clear;
FHTTP.Request.RawHeaders.Clear;
Result := Self;
end;
function TRESTClient.SendHTTPCommand(const ACommand: THttpCommand;
2013-11-05 14:57:50 +01:00
const AAccept, AContentType, AUrl: string; ABodyParams: TStrings)
2013-10-30 00:48:23 +01:00
: IRESTResponse;
begin
Result := TRESTResponse.Create;
FHTTP.Request.RawHeaders.Clear;
FHTTP.Request.CustomHeaders.Clear;
FHTTP.Request.Accept := AAccept;
FHTTP.Request.ContentType := AContentType;
HandleRequestCookies;
try
case ACommand of
httpGET:
begin
Result.Body.Position := 0;
FHTTP.Get(AUrl, Result.Body);
end;
httpPOST:
begin
if GetMultipartFormData.Size = 0 then
begin
Result.Body.Position := 0;
GetRawBody; // creates the body
FHTTP.Post(AUrl, FRawBody, Result.Body);
end
else
begin
FHTTP.Post(AUrl, GetMultipartFormData, Result.Body);
GetMultipartFormData.Clear;
end;
end;
httpPUT:
begin
if GetMultipartFormData.Size <> 0
then { TODO -oDaniele -cGeneral : Rework please!!! }
2013-10-30 00:48:23 +01:00
raise Exception.Create('Only POST can Send Files');
Result.Body.Position := 0;
if Assigned(ABodyParams) and (ABodyParams.Count > 0) then
begin
GetRawBody.Size := 0;
2014-04-02 20:27:17 +02:00
GetRawBody.WriteString(EncodeQueryStringParams(ABodyParams, false));
2013-10-30 00:48:23 +01:00
end;
FHTTP.Put(AUrl, FRawBody, Result.Body);
end;
httpDELETE:
begin
Result.Body.Position := 0;
FHTTP.Delete(AUrl);
GetRawBody.Size := 0;
end;
end;
except
on E: EIdHTTPProtocolException do
begin
Result.Body.WriteString(E.ErrorMessage);
end;
end;
HandleCookies;
Result.SetResponseCode(FHTTP.Response.ResponseCode);
Result.SetResponseText(FHTTP.Response.ResponseText);
Result.SetHeaders(FHTTP.Response.RawHeaders);
end;
function TRESTClient.SendHTTPCommandWithBody(const ACommand: THttpCommand;
2014-06-27 15:30:39 +02:00
const AAccept, AContentType, AUrl: string; ABodyString: string): IRESTResponse;
2013-10-30 00:48:23 +01:00
begin
Result := TRESTResponse.Create;
FHTTP.Request.RawHeaders.Clear;
FHTTP.Request.CustomHeaders.Clear;
FHTTP.Request.Accept := AAccept;
FHTTP.Request.ContentType := AContentType;
HandleRequestCookies;
try
case ACommand of
httpGET:
begin
FHTTP.Get(AUrl, Result.Body);
end;
httpPOST:
begin
if GetMultipartFormData.Size <> 0 then
raise Exception.Create('This method cannot send files');
RawBody.Position := 0;
FRawBody.Size := 0;
FRawBody.WriteString(UTF8Encode(ABodyString));
2013-10-30 00:48:23 +01:00
FHTTP.Post(AUrl, FRawBody, Result.Body);
end;
2014-03-24 17:37:08 +01:00
httpPATCH:
begin
raise Exception.Create
('Sorry, PATCH is not supported by the RESTClient because is not supportd by the TidHTTP');
end;
2013-10-30 00:48:23 +01:00
httpPUT:
begin
RawBody.Position := 0;
FRawBody.Size := 0;
FRawBody.WriteString(UTF8Encode(ABodyString));
2013-10-30 00:48:23 +01:00
FHTTP.Put(AUrl, FRawBody, Result.Body);
end;
httpDELETE:
begin
FHTTP.Delete(AUrl);
RawBody.Size := 0;
end;
end;
except
on E: EIdHTTPProtocolException do
begin
Result.Body.WriteString(E.ErrorMessage);
end;
end;
HandleCookies;
Result.SetResponseCode(FHTTP.Response.ResponseCode);
Result.SetResponseText(FHTTP.Response.ResponseText);
Result.SetHeaders(FHTTP.Response.RawHeaders);
end;
procedure TRESTClient.SetAccept(const Value: string);
begin
FAccept := Value;
end;
procedure TRESTClient.SetBodyParams(const Value: TStringlist);
begin
FBodyParams := Value;
end;
procedure TRESTClient.SetConnectionTimeout(const Value: Integer);
begin
FHTTP.ConnectTimeout := Value;
end;
procedure TRESTClient.SetContentType(const Value: string);
begin
FContentType := Value;
end;
procedure TRESTClient.SetQueryStringParams(const Value: TStringlist);
begin
FQueryStringParams := Value;
end;
procedure TRESTClient.SetSessionID(const Value: string);
begin
FLastSessionID := Value;
if Assigned(FHTTP.CookieManager) then
FHTTP.CookieManager.CookieCollection.Clear;
end;
procedure TRESTClient.SetReadTimeout(const Value: Integer);
begin
FHTTP.ReadTimeout := Value;
end;
procedure TRESTClient.SetRequestHeaders(const Value: TStringlist);
begin
FRequestHeaders.Assign(Value);
end;
{ TRESTResponse }
function TRESTResponse.Body: TStringStream;
begin
Result := FBody;
end;
function TRESTResponse.BodyAsJsonObject: TJSONObject;
begin
Result := BodyAsJsonValue as TJSONObject;
end;
function TRESTResponse.BodyAsJsonValue: TJSONValue;
2013-10-30 00:48:23 +01:00
begin
try
if not Assigned(FBodyAsJSONValue) then
2013-10-30 00:48:23 +01:00
begin
if BodyAsString = '' then
FBodyAsJSONValue := nil
2013-10-30 00:48:23 +01:00
else
begin
try
FBodyAsJSONValue := TJSONObject.ParseJSONValue(BodyAsString);
// if Assigned(V) then
// FBodyAsJSONObject := V as TJSONObject;
2013-10-30 00:48:23 +01:00
except
FBodyAsJSONValue := nil;
2013-10-30 00:48:23 +01:00
end;
end;
end;
Result := FBodyAsJSONValue;
2013-10-30 00:48:23 +01:00
except
on E: Exception do
begin
raise;
end;
end;
end;
function TRESTResponse.BodyAsString: string;
var
ss: TStringStream;
begin
2013-11-05 14:57:50 +01:00
if FContentEncoding.IsEmpty then
FContentEncoding := 'UTF-8';
ss := TStringStream.Create('', TEncoding.GetEncoding(FContentEncoding));
2013-10-30 00:48:23 +01:00
try
FBody.Position := 0;
FBody.SaveToStream(ss);
Result := ss.DataString;
finally
ss.Free;
end;
end;
constructor TRESTResponse.Create;
begin
inherited;
FHeaders := TStringlist.Create;
FBody := TStringStream.Create('', TEncoding.UTF8);
FBodyAsJSONValue := nil;
2013-10-30 00:48:23 +01:00
end;
destructor TRESTResponse.Destroy;
begin
FreeAndNil(FBodyAsJSONValue);
2013-10-30 00:48:23 +01:00
FHeaders.Free;
FBody.Free;
inherited;
end;
2014-06-27 15:30:39 +02:00
function TRESTResponse.GetContentEncoding: string;
2013-11-05 14:57:50 +01:00
begin
Result := FContentEncoding;
end;
2014-06-27 15:30:39 +02:00
function TRESTResponse.GetContentType: string;
2013-11-05 14:57:50 +01:00
begin
Result := FContentType;
end;
2014-06-27 15:30:39 +02:00
function TRESTResponse.GetHeader(const Value: string): string;
2013-11-05 14:57:50 +01:00
var
2014-06-27 15:30:39 +02:00
s: string;
2013-11-05 14:57:50 +01:00
begin
if Assigned(FHeaders) and (FHeaders.Count > 0) then
begin
for s in FHeaders do
begin
if s.StartsWith(Value + ':', true) then
begin
Exit(s);
end;
end;
end
else
begin
Result := '';
end;
end;
function TRESTResponse.GetHeaderValue(const Name: string): string;
var
s: string;
arr: TArray<string>;
begin
Result := '';
for s in Self.Headers do
begin
arr := s.Split([':'], 2);
if SameText(arr[0].trim, name) then
begin
Result := arr[1].trim;
Break;
end;
end;
end;
2013-10-30 00:48:23 +01:00
function TRESTResponse.Headers: TStringlist;
begin
Result := FHeaders;
end;
function TRESTResponse.ResponseCode: Word;
begin
Result := FResponseCode;
end;
function TRESTResponse.ResponseText: string;
begin
Result := FResponseText;
end;
procedure TRESTResponse.SetHeaders(AHeaders: TStrings);
2013-11-05 14:57:50 +01:00
var
CT: TArray<string>;
2014-06-27 15:30:39 +02:00
C: string;
2013-10-30 00:48:23 +01:00
begin
FHeaders.Assign(AHeaders);
2013-11-05 14:57:50 +01:00
C := GetHeader('content-type');
CT := C.Split([':'])[1].Split([';']);
FContentType := trim(CT[0]);
2013-11-05 14:57:50 +01:00
FContentEncoding := 'UTF-8'; // default encoding
if Length(CT) > 1 then
begin
if CT[1].trim.StartsWith('charset', true) then
begin
FContentEncoding := CT[1].trim.Split(['='])[1].trim;
end;
end;
2013-10-30 00:48:23 +01:00
end;
procedure TRESTResponse.SetResponseCode(AResponseCode: Word);
begin
FResponseCode := AResponseCode;
end;
procedure TRESTResponse.SetResponseText(AResponseText: string);
begin
FResponseText := AResponseText;
end;
end.