delphimvcframework/sources/MVCFramework.RESTClient.pas

1480 lines
46 KiB
ObjectPascal
Raw Normal View History

// ***************************************************************************
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2020 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// Collaborators on this file:
// Jo<4A>o Ant<6E>nio Duarte (https://github.com/joaoduarte19)
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// *************************************************************************** }
2013-10-30 00:48:23 +01:00
unit MVCFramework.RESTClient;
{$I dmvcframework.inc}
2013-10-30 00:48:23 +01:00
interface
uses
2015-12-18 20:59:40 +01:00
System.SysUtils,
System.Classes,
REST.Client,
REST.Types,
2020-08-09 02:18:36 +02:00
MVCFramework.RESTClient.Intf,
MVCFramework.Serializer.Intf,
2020-08-09 02:18:36 +02:00
MVCFramework.Serializer.Commons,
MVCFramework.RESTClient.Indy,
2020-08-09 02:18:36 +02:00
Data.DB,
System.Rtti,
System.TypInfo,
System.Net.HttpClient;
2013-10-30 00:48:23 +01:00
type
/// <summary>
/// Alias for the Indy-based TRESTClient. The implementation of TRESTClient has been discontinued, it remains for
/// compatibility only.
/// </summary>
TRESTClient = MVCFramework.RESTClient.Indy.TRESTClient deprecated
'Moved to the MVCFramework.RESTClient.Indy unit. It is highly recommended to migrate to the TMVCRESTClient implementation.';
IRESTResponse = MVCFramework.RESTClient.Indy.IRESTResponse deprecated
'Moved to the MVCFramework.RESTClient.Indy unit. It is highly recommended to migrate to the TMVCRESTClient implementation.';
/// <summary>
/// Provides access to delphi RESTClient library types without the need to use the REST.Types unit.
/// </summary>
TRESTContentType = REST.Types.TRESTContentType;
TCookie = System.Net.HttpClient.TCookie;
TCookies = System.Net.HttpClient.TCookies;
2020-08-26 22:13:18 +02:00
THTTPSecureProtocol = System.Net.HttpClient.THTTPSecureProtocol;
THTTPSecureProtocols = System.Net.HttpClient.THTTPSecureProtocols;
/// <summary>
/// Encapsulates the methods of the delphi native RESTClient library.
/// </summary>
TMVCRESTClient = class(TInterfacedObject, IMVCRESTClient)
private
2020-08-09 02:18:36 +02:00
fRttiContext: TRttiContext;
fRESTClient: TCustomRESTClient;
fRESTRequest: TCustomRESTRequest;
fRESTResponse: TCustomRESTResponse;
fSerializer: IMVCSerializer;
fNextRequestIsAsync: Boolean;
fAsyncCompletionHandler: TProc<IMVCRESTResponse>;
fAsyncCompletionHandlerWithError: TProc<Exception>;
fAsyncSynchronized: Boolean;
2020-08-09 02:18:36 +02:00
procedure ClearRESTParams(const aRESTParamKind: TRESTRequestParameterKind);
function ConvertMVCPathParamsToRESTParams(const aResource: string): string;
function CloneRESTClient: IMVCRESTClient;
2020-08-09 02:18:36 +02:00
function ObjectIsList(aObject: TObject): Boolean;
function SerializeObject(aObject: TObject): string;
function PreEncodeURL(const aURL: string): string;
{$IF not defined(SYDNEYORBETTER)}
function GetResponseCookies: TArray<TCookie>;
{$ENDIF}
procedure ExecuteAsyncRESTRequest;
function ExecuteRESTRequest(const aMethod: TRESTRequestMethod): IMVCRESTResponse;
2013-10-30 00:48:23 +01:00
public
constructor Create;
2013-10-30 00:48:23 +01:00
destructor Destroy; override;
class function New: IMVCRESTClient;
{ IMVCRESTClient }
2020-08-14 00:43:25 +02:00
function BaseURL(const aBaseURL: string): IMVCRESTClient; overload;
function BaseURL(const aHost: string; const aPort: Integer): IMVCRESTClient; overload;
function BaseURL: string; overload;
function RaiseExceptionOn500(const aRaiseExceptionOn500: Boolean): IMVCRESTClient; overload;
function RaiseExceptionOn500: Boolean; overload;
function ProxyServer(const aProxyServer: string): IMVCRESTClient; overload;
function ProxyServer: string; overload;
function ProxyPort(const aProxyPort: Integer): IMVCRESTClient; overload;
function ProxyPort: Integer; overload;
function ProxyUsername(const aProxyUsername: string): IMVCRESTClient; overload;
function ProxyUsername: string; overload;
function ProxyPassword(const aProxyPassword: string): IMVCRESTClient; overload;
function ProxyPassword: string; overload;
2020-08-26 22:13:18 +02:00
function SecureProtocols(const aSecureProtocols: THTTPSecureProtocols): IMVCRESTClient; overload;
function SecureProtocols: THTTPSecureProtocols; overload;
function UserAgent(const aUserAgent: string): IMVCRESTClient; overload;
function UserAgent: string; overload;
/// <summary>
/// Clears all parameters, except authorization headers and cookies. This method is executed after each request
/// is completed.
/// </summary>
function ClearAllParams: IMVCRESTClient;
/// <summary>
/// Connection timeout in milliseconds to be used for the requests.
/// </summary>
function ConnectTimeout(const aConnectTimeout: Integer): IMVCRESTClient; overload;
function ConnectTimeout: Integer; overload;
/// <summary>
/// Response reading timeout in milliseconds to be used for the requests.
/// </summary>
function ReadTimeout(const aReadTimeout: Integer): IMVCRESTClient; overload;
function ReadTimeout: Integer; overload;
/// <summary>
/// Add basic authorization header. Authorization = Basic &lt;Username:Password&gt; (encoded in Base64)
/// </summary>
function SetBasicAuthorization(const aUsername, aPassword: string): IMVCRESTClient;
/// <summary>
/// Add bearer authorization header. Authorization = Bearer &lt;Token&gt;
/// </summary>
function SetBearerAuthorization(const aToken: string): IMVCRESTClient;
/// <summary>
/// Add a header.
/// </summary>
/// <param name="aName">
/// Header name
/// </param>
/// <param name="aValue">
/// Header value
/// </param>
/// <param name="aDoNotEncode">
/// Indicates whether the value of this header should be used as is (True), or encoded by the component (False)
/// </param>
function AddHeader(const aName, aValue: string; const aDoNotEncode: Boolean = False): IMVCRESTClient; overload;
function HeaderValue(const aName: string): string;
/// <summary>
/// Clears all headers.
/// </summary>
function ClearHeaders: IMVCRESTClient;
function AllowCookies(const aAllowCookies: Boolean): IMVCRESTClient; overload;
function AllowCookies: Boolean; overload;
/// <summary>
/// Add a cookie header.
/// </summary>
function AddCookie(const aName, aValue: string): IMVCRESTClient;
/// <summary>
/// Clear all cookies.
/// </summary>
function ClearCookies: IMVCRESTClient;
/// <summary>
/// Add a URL segment parameter. The parameters of your url path may be enclosed in braces or in
/// parentheses starting with a money sign. <c>/api/{param1}/($param2)</c>
/// </summary>
/// <param name="aName">
/// Parameter name
/// </param>
/// <param name="aValue">
/// Parameter value
/// </param>
function AddPathParam(const aName, aValue: string): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: Integer): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: Int64): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: TGUID): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: TDateTime): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: TDate): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: TTime): IMVCRESTClient; overload;
function AddPathParam(const aName: string; aValue: Double): IMVCRESTClient; overload;
function ClearPathParams: IMVCRESTClient;
/// <summary>
/// Add a QueryString parameter. <c>/api/person?para1=value&amp;param2=value</c>
/// </summary>
function AddQueryStringParam(const aName, aValue: string): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: Integer): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: Int64): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: TGUID): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: TDateTime): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: TDate): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: TTime): IMVCRESTClient; overload;
function AddQueryStringParam(const aName: string; aValue: Double): IMVCRESTClient; overload;
function ClearQueryParams: IMVCRESTClient;
function Accept(const aAccept: string): IMVCRESTClient; overload;
function Accept: string; overload;
function AcceptCharset(const aAcceptCharset: string): IMVCRESTClient; overload;
function AcceptCharset: string; overload;
function AcceptEncoding(const aAcceptEncoding: string): IMVCRESTClient; overload;
function AcceptEncoding: string; overload;
function HandleRedirects(const aHandleRedirects: Boolean): IMVCRESTClient; overload;
function HandleRedirects: Boolean; overload;
function FallbackCharsetEncoding(const aFallbackCharsetEncoding: string): IMVCRESTClient; overload;
function FallbackCharsetEncoding: string; overload;
function Resource(const aResource: string): IMVCRESTClient; overload;
function Resource: string; overload;
function URLAlreadyEncoded(const aURLAlreadyEncoded: Boolean): IMVCRESTClient; overload;
function URLAlreadyEncoded: Boolean; overload;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Add a body to the requisition.
/// </summary>
/// <param name="aBody">
/// Body in string format.
/// </param>
/// <param name="aContentType">
/// Body content type.
/// </param>
function AddBody(const aBody: string; const aDoNotEncode: Boolean = False;
const aContentType: TRESTContentType = TRESTContentType.ctNone): IMVCRESTClient; overload;
/// <summary>
/// Add a body to the requisition
/// </summary>
/// <param name="aBodyStream">
/// Body in Stream format
/// </param>
/// <param name="aContentType">
/// Body content type
/// </param>
/// <param name="aOwnsStream">
/// If OwnsStream is true, Stream will be destroyed by IMVCRESTClient.
/// </param>
function AddBody(aBodyStream: TStream; const aContentType: TRESTContentType = TRESTContentType.ctNone;
const aOwnsStream: Boolean = True): IMVCRESTClient; overload;
/// <summary>
/// Add a body to the requisition
/// </summary>
/// <param name="aBodyObject">
/// Body in Object format. The object will be serialized to a JSON string.
/// </param>
/// <param name="aOwnsObject">
/// If OwnsObject is true, BodyObject will be destroyed by IMVCRESTClient.
/// </param>
function AddBody(aBodyObject: TObject; const aOwnsObject: Boolean = True): IMVCRESTClient; overload;
function ClearBody: IMVCRESTClient;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Adds a file as the request body. Several files can be added in the same request. In this case the request
/// will be of the multipart/form-data type
/// </summary>
/// <param name="aName">
/// Field name
/// </param>
/// <param name="aFileName">
/// File path
/// </param>
/// <param name="aContentType">
/// File content type
/// </param>
2020-08-09 02:18:36 +02:00
function AddFile(const aName, aFileName: string;
const aContentType: TRESTContentType = TRESTContentType.ctNone): IMVCRESTClient; overload;
function AddFile(const aFileName: string;
const aContentType: TRESTContentType = TRESTContentType.ctNone): IMVCRESTClient; overload;
function ClearFiles: IMVCRESTClient;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Executes the next request asynchronously.
/// </summary>
/// <param name="aCompletionHandler">
/// An anonymous method that will be run after the execution completed.
/// </param>
/// <param name="aSynchronized">
/// Specifies if aCompletioHandler will be run in the main thread's (True) or execution thread's (False) context.
/// </param>
/// <param name="aCompletionHandlerWithError">
/// An anonymous method that will be run if an exception is raised during execution.
/// </param>
function Async(aCompletionHandler: TProc<IMVCRESTResponse>; aCompletionHandlerWithError: TProc<Exception> = nil;
const aSynchronized: Boolean = False): IMVCRESTClient;
/// <summary>
/// Execute a Get request.
/// </summary>
function Get(const aResource: string): IMVCRESTResponse; overload;
function Get: IMVCRESTResponse; overload;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Execute a Post request.
/// </summary>
function Post(const aResource: string; aBody: TObject; const aOwnsBody: Boolean = True): IMVCRESTResponse; overload;
function Post(const aResource: string; const aBody: string = ''; const aDoNotEncode: Boolean = False;
const aContentType: TRESTContentType = TRESTContentType.ctAPPLICATION_JSON): IMVCRESTResponse; overload;
function Post: IMVCRESTResponse; overload;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Execute a Patch request.
/// </summary>
function Patch(const aResource: string; aBody: TObject;
const aOwnsBody: Boolean = True): IMVCRESTResponse; overload;
function Patch(const aResource: string; const aBody: string = ''; const aDoNotEncode: Boolean = False;
const aContentType: TRESTContentType = TRESTContentType.ctAPPLICATION_JSON): IMVCRESTResponse; overload;
function Patch: IMVCRESTResponse; overload;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Execute a Put request.
/// </summary>
function Put(const aResource: string; aBody: TObject; const aOwnsBody: Boolean = True): IMVCRESTResponse; overload;
function Put(const aResource: string; const aBody: string = ''; const aDoNotEncode: Boolean = False;
const aContentType: TRESTContentType = TRESTContentType.ctAPPLICATION_JSON): IMVCRESTResponse; overload;
function Put: IMVCRESTResponse; overload;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Execute a Delete request.
/// </summary>
function Delete(const aResource: string): IMVCRESTResponse; overload;
function Delete: IMVCRESTResponse; overload;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Serialize the current dataset record and execute a POST request.
/// </summary>
2020-08-09 02:18:36 +02:00
function DataSetInsert(const aResource: string; aDataSet: TDataSet; const aIgnoredFields: TMVCIgnoredList = [];
const aNameCase: TMVCNameCase = ncAsIs): IMVCRESTResponse;
/// <summary>
/// Serialize the current dataset record and execute a PUT request.
/// </summary>
function DataSetUpdate(const aResource, aKeyValue: string; aDataSet: TDataSet; const aIgnoredFields: TMVCIgnoredList = [];
const aNameCase: TMVCNameCase = ncAsIs): IMVCRESTResponse;
/// <summary>
/// Delete the current dataset record by executing a delete request.
/// </summary>
function DataSetDelete(const aResource, aKeyValue: string): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
/// <summary>
/// Register a custom serializer to the RESTClient serializer.
/// </summary>
function RegisterTypeSerializer(const aTypeInfo: PTypeInfo; aInstance: IMVCTypeSerializer): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
end;
/// <summary>
/// Provides access to the REST request response.
/// </summary>
TMVCRESTResponse = class(TInterfacedObject, IMVCRESTResponse)
private
fSuccess: Boolean;
fStatusCode: Integer;
fStatusText: string;
2020-08-09 02:18:36 +02:00
fErrorMessage: string;
fHeaders: TStrings;
fCookies: TCookies;
2020-08-09 02:18:36 +02:00
fServer: string;
fFullRequestURI: string;
fContentType: string;
fContentEncoding: string;
fContentLength: Integer;
fContent: string;
fRawBytes: TBytes;
2020-08-09 02:18:36 +02:00
procedure FillRESTResponse(aRESTResponse: TCustomRESTResponse);
2015-12-18 20:59:40 +01:00
public
2020-08-09 02:18:36 +02:00
constructor Create(aRESTResponse: TCustomRESTResponse);
2015-12-18 20:59:40 +01:00
destructor Destroy; override;
{$IF not defined(SYDNEYORBETTER)}
procedure SetCookies(aCookies: TArray<TCookie>);
{$ENDIF}
{ IMVCRESTResponse }
2020-08-09 02:18:36 +02:00
function Success: Boolean;
function StatusCode: Integer;
function StatusText: string;
function ErrorMessage: string;
function Headers: TStrings;
function HeaderValue(const aName: string): string;
function Cookies: TCookies;
function CookieByName(const aName: string): TCookie;
2020-08-09 02:18:36 +02:00
function Server: string;
function FullRequestURI: string;
function ContentType: string;
function ContentEncoding: string;
function ContentLength: Integer;
function Content: string;
function RawBytes: TBytes;
procedure SaveContentToStream(aStream: TStream);
procedure SaveContentToFile(const aFileName: string);
end;
EMVCRESTClientException = class(Exception);
2020-08-09 02:18:36 +02:00
implementation
2015-12-18 20:59:40 +01:00
uses
MVCFramework.Serializer.JsonDataObjects,
2020-08-09 02:18:36 +02:00
System.NetEncoding,
System.RegularExpressions,
REST.HttpClient;
const
DEFAULT_ACCEPT_ENCODING = 'gzip';
DEFAULT_BODY_NAME = 'body';
DEFAULT_FILE_NAME = 'file';
AUTHORIZATION_HEADER = 'Authorization';
BASIC_AUTH_PREFIX = 'Basic ';
BEARER_AUTH_PREFIX = 'Bearer ';
HEADER_RESPONSE_COOKIES = 'Cookies';
PATH_UNSAFE_CHARS: TURLEncoding.TUnsafeChars = [Ord('"'), Ord('<'), Ord('>'), Ord('^'), Ord('`'), Ord('{'),
Ord('}'), Ord('|'), Ord('/'), Ord('\'), Ord('?'), Ord('#'), Ord('+'), Ord('.')];
type
THackCustomRESTRequest = class(TCustomRESTRequest)
private
protected
procedure DoApplyURLSegments(const aParamList: TRESTRequestParameterArray; var aURL: string); override;
end;
{ TMVCRESTClient }
function TMVCRESTClient.Accept(const aAccept: string): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := Self;
fRESTRequest.Accept := aAccept;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.Accept: string;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := fRESTRequest.Accept;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AcceptCharset: string;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := fRESTRequest.AcceptCharset;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AcceptCharset(const aAcceptCharset: string): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
fRESTRequest.AcceptCharset := aAcceptCharset;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AcceptEncoding: string;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := fRESTRequest.AcceptEncoding;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AcceptEncoding(const aAcceptEncoding: string): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := Self;
fRESTRequest.AcceptEncoding := aAcceptEncoding;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddBody(const aBody: string; const aDoNotEncode: Boolean;
const aContentType: TRESTContentType): IMVCRESTClient;
var
lBodyName: string;
lOptions: TRESTRequestParameterOptions;
2015-12-18 20:59:40 +01:00
begin
Result := Self;
// A body does not have a specific name, but as names need to be unique, we are using a GUID here
lBodyName := TGUID.NewGuid.ToString;
lBodyName := lBodyName.Replace('{', '', [rfReplaceAll]);
lBodyName := lBodyName.Replace('}', '', [rfReplaceAll]);
lBodyName := lBodyName.Replace('-', '', [rfReplaceAll]);
lBodyName := DEFAULT_BODY_NAME + lBodyName;
if aDoNotEncode then
lOptions := [TRESTRequestParameterOption.poDoNotEncode]
else
lOptions := [];
fRESTRequest.Params.AddItem(lBodyName, aBody, TRESTRequestParameterKind.pkREQUESTBODY, lOptions, aContentType);
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddBody(aBodyStream: TStream; const aContentType: TRESTContentType;
const aOwnsStream: Boolean): IMVCRESTClient;
2020-08-09 02:18:36 +02:00
var
lOwnsStream: TRESTObjectOwnership;
2015-12-18 20:59:40 +01:00
begin
if aBodyStream = nil then
raise EMVCRESTClientException.Create('You need a valid body!');
2015-12-18 20:59:40 +01:00
Result := Self;
2020-08-09 02:18:36 +02:00
if aOwnsStream then
lOwnsStream := TRESTObjectOwnership.ooREST
else
lOwnsStream := TRESTObjectOwnership.ooApp;
fRESTRequest.AddBody(aBodyStream, aContentType, lOwnsStream);
end;
function TMVCRESTClient.AddBody(aBodyObject: TObject; const aOwnsObject: Boolean): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
if aBodyObject = nil then
raise EMVCRESTClientException.Create('You need a valid body!');
2020-08-09 02:18:36 +02:00
Result := Self;
AddBody(SerializeObject(aBodyObject), False, TRESTContentType.ctAPPLICATION_JSON);
2020-08-09 02:18:36 +02:00
if aOwnsObject then
aBodyObject.Free;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddCookie(const aName, aValue: string): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
fRESTRequest.AddParameter(aName, aValue, TRESTRequestParameterKind.pkCOOKIE);
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddFile(const aName, aFileName: string; const aContentType: TRESTContentType): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := Self;
fRESTRequest.AddFile(aName, aFileName, aContentType);
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddFile(const aFileName: string; const aContentType: TRESTContentType): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
Result := AddFile(DEFAULT_FILE_NAME, aFileName, aContentType);
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddHeader(const aName, aValue: string; const aDoNotEncode: Boolean): IMVCRESTClient;
var
lOptions: TRESTRequestParameterOptions;
2013-10-30 00:48:23 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := Self;
if aDoNotEncode then
lOptions := [TRESTRequestParameterOption.poDoNotEncode]
else
lOptions := [];
fRESTRequest.AddParameter(aName, aValue, TRESTRequestParameterKind.pkHTTPHEADER, lOptions);
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: TGUID): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, aValue.ToString);
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: Int64): IMVCRESTClient;
2014-04-10 13:56:23 +02:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, aValue.ToString);
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: Integer): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, aValue.ToString);
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddPathParam(const aName, aValue: string): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
Result := Self;
fRESTRequest.AddParameter(aName, aValue, TRESTRequestParameterKind.pkURLSEGMENT);
2014-04-10 13:56:23 +02:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: Double): IMVCRESTClient;
2014-04-10 13:56:23 +02:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, aValue.ToString);
2014-04-10 13:56:23 +02:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: TTime): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, TimeToISOTime(aValue));
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: TDateTime): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, DateTimeToISOTimeStamp(aValue));
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddPathParam(const aName: string; aValue: TDate): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddPathParam(aName, DateToISODate(aValue));
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: TDate): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, DateToISODate(aValue));
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: TDateTime): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, DateTimeToISOTimeStamp(aValue));
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: TTime): IMVCRESTClient;
2015-12-18 20:59:40 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, TimeToISOTime(aValue));
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: Double): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, aValue.ToString);
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AllowCookies: Boolean;
begin
Result := fRESTClient.AllowCookies;
end;
function TMVCRESTClient.AllowCookies(const aAllowCookies: Boolean): IMVCRESTClient;
begin
Result := Self;
fRESTClient.AllowCookies := aAllowCookies;
end;
function TMVCRESTClient.Async(aCompletionHandler: TProc<IMVCRESTResponse>; aCompletionHandlerWithError: TProc<Exception>;
const aSynchronized: Boolean): IMVCRESTClient;
begin
Result := Self;
fNextRequestIsAsync := True;
fAsyncCompletionHandler := aCompletionHandler;
fAsyncCompletionHandlerWithError := aCompletionHandlerWithError;
fAsyncSynchronized := aSynchronized;
end;
function TMVCRESTClient.BaseURL(const aHost: string; const aPort: Integer): IMVCRESTClient;
begin
Result := BaseURL(aHost + ':' + aPort.ToString);
end;
function TMVCRESTClient.BaseURL(const aBaseURL: string): IMVCRESTClient;
var
lBaseURL: string;
begin
Result := Self;
lBaseURL := aBaseURL;
if not lBaseURL.Contains('://') then
lBaseURL := 'http://' + lBaseURL;
fRESTClient.BaseURL := PreEncodeURL(lBaseURL);
end;
function TMVCRESTClient.BaseURL: string;
begin
Result := fRESTClient.BaseURL;
end;
function TMVCRESTClient.AddQueryStringParam(const aName, aValue: string): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
fRESTRequest.AddParameter(aName, aValue, TRESTRequestParameterKind.pkQUERY);
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: Integer): IMVCRESTClient;
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, aValue.ToString);
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: TGUID): IMVCRESTClient;
2015-12-22 12:17:13 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, aValue.ToString);
2015-12-22 12:17:13 +01:00
end;
function TMVCRESTClient.AddQueryStringParam(const aName: string; aValue: Int64): IMVCRESTClient;
2015-12-22 12:17:13 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := AddQueryStringParam(aName, aValue.ToString);
2015-12-22 12:17:13 +01:00
end;
function TMVCRESTClient.ClearAllParams: IMVCRESTClient;
var
lAuthHeader: TRESTRequestParameter;
lLastResource: string;
begin
Result := Self;
fRESTClient.HandleRedirects := True;
fRESTClient.RaiseExceptionOn500 := False;
fRESTClient.AllowCookies := True;
// If the authorization header exists, it will be extracted from the list of parameters to be added later.
lAuthHeader := fRESTRequest.Params.ParameterByName(AUTHORIZATION_HEADER);
if Assigned(lAuthHeader) then
lAuthHeader.Collection := nil;
// Saves the last resource of the request
lLastResource := fRESTRequest.Resource;
fRESTRequest.ResetToDefaults;
fRESTRequest.AutoCreateParams := False;
fRESTRequest.AcceptEncoding := DEFAULT_ACCEPT_ENCODING;
2020-08-20 23:35:28 +02:00
if Assigned(lAuthHeader) then
lAuthHeader.Collection := fRESTRequest.Params;
fRESTRequest.Resource := lLastResource;
2020-08-20 23:35:28 +02:00
fNextRequestIsAsync := False;;
fAsyncCompletionHandler := nil;
fAsyncCompletionHandlerWithError := nil;
fAsyncSynchronized := False;
end;
function TMVCRESTClient.ClearBody: IMVCRESTClient;
2020-08-09 02:18:36 +02:00
begin
Result := Self;
fRESTRequest.ClearBody;
end;
type
THackRESTClient = class(TCustomRESTClient);
function TMVCRESTClient.ClearCookies: IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
ClearRESTParams(TRESTRequestParameterKind.pkCOOKIE);
// It is necessary to recreate the RESTClient's internal HttpClient so that stored cookies are deleted.
THackRESTClient(fRESTClient).CreateHttpClient;
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.ClearFiles: IMVCRESTClient;
2020-08-09 02:18:36 +02:00
begin
Result := Self;
ClearRESTParams(TRESTRequestParameterKind.pkFILE);
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.ClearHeaders: IMVCRESTClient;
2014-03-24 17:37:08 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
ClearRESTParams(TRESTRequestParameterKind.pkHTTPHEADER);
2014-03-24 17:37:08 +01:00
end;
function TMVCRESTClient.ClearPathParams: IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
ClearRESTParams(TRESTRequestParameterKind.pkURLSEGMENT);
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.ClearQueryParams: IMVCRESTClient;
2020-08-09 02:18:36 +02:00
begin
Result := Self;
ClearRESTParams(TRESTRequestParameterKind.pkQUERY);
end;
procedure TMVCRESTClient.ClearRESTParams(const aRESTParamKind: TRESTRequestParameterKind);
2013-10-30 00:48:23 +01:00
var
I: Integer;
2013-10-30 00:48:23 +01:00
begin
for I := Pred(fRESTRequest.Params.Count) downto 0 do
2013-10-30 00:48:23 +01:00
begin
2020-08-09 02:18:36 +02:00
if (fRESTRequest.Params[I].Kind = aRESTParamKind) then
fRESTRequest.Params.Delete(I);
2013-11-11 12:23:49 +01:00
end;
end;
2020-08-20 23:35:28 +02:00
function TMVCRESTClient.CloneRESTClient: IMVCRESTClient;
begin
Result := TMVCRESTClient.New
.BaseURL(BaseURL)
.RaiseExceptionOn500(RaiseExceptionOn500)
.ProxyServer(ProxyServer)
.ProxyPort(ProxyPort)
.ProxyUsername(ProxyUsername)
.ProxyPassword(ProxyPassword)
.ProxyServer(ProxyServer)
.UserAgent(UserAgent)
.ConnectTimeout(ConnectTimeout)
.ReadTimeout(ReadTimeout)
2020-08-20 23:35:28 +02:00
.Accept(Accept)
.AcceptCharset(AcceptCharset)
.AcceptEncoding(AcceptEncoding)
.HandleRedirects(HandleRedirects)
.FallbackCharsetEncoding(FallbackCharsetEncoding)
.AllowCookies(AllowCookies)
2020-08-20 23:35:28 +02:00
.Resource(Resource)
.URLAlreadyEncoded(URLAlreadyEncoded);
TMVCRESTClient(Result).fRESTRequest.Method := fRESTRequest.Method;
2020-08-20 23:35:28 +02:00
TMVCRESTClient(Result).fRESTRequest.Params.Assign(fRESTRequest.Params);
end;
function TMVCRESTClient.ConnectTimeout(const aConnectTimeout: Integer): IMVCRESTClient;
begin
Result := Self;
{$IF defined(SYDNEYORBETTER)}
fRESTRequest.ConnectTimeout := aConnectTimeout;
{$ELSE}
fRESTRequest.Timeout := aConnectTimeout;
{$ENDIF}
end;
function TMVCRESTClient.ConnectTimeout: Integer;
begin
{$IF defined(SYDNEYORBETTER)}
Result := fRESTRequest.ConnectTimeout;
{$ELSE}
Result := fRESTRequest.Timeout;
{$ENDIF}
end;
function TMVCRESTClient.ConvertMVCPathParamsToRESTParams(const aResource: string): string;
2020-08-09 02:18:36 +02:00
begin
Result := TRegEx.Replace(aResource, '(\([($])([\w_]+)([)])', '{\2}', [roIgnoreCase]);
end;
constructor TMVCRESTClient.Create;
2015-12-22 12:17:13 +01:00
begin
inherited Create;
2015-12-22 12:17:13 +01:00
fRESTClient := TCustomRESTClient.Create(nil);
fRESTRequest := THackCustomRESTRequest.Create(nil);
fRESTResponse := TCustomRESTResponse.Create(nil);
2015-12-22 12:17:13 +01:00
fRESTRequest.Client := fRESTClient;
fRESTRequest.Response := fRESTResponse;
fSerializer := TMVCJsonDataObjectsSerializer.Create;
2020-08-09 02:18:36 +02:00
fRttiContext := TRttiContext.Create;
ClearAllParams;
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.DataSetDelete(const aResource, aKeyValue: string): IMVCRESTResponse;
var
lResource: string;
2020-08-09 02:18:36 +02:00
begin
lResource := aResource + '/' + aKeyValue;
Result := Delete(lResource);
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.DataSetInsert(const aResource: string; aDataSet: TDataSet; const aIgnoredFields: TMVCIgnoredList;
const aNameCase: TMVCNameCase): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Result := Post(aResource, fSerializer.SerializeDataSetRecord(aDataSet, aIgnoredFields, aNameCase));
end;
function TMVCRESTClient.DataSetUpdate(const aResource, aKeyValue: string; aDataSet: TDataSet;
const aIgnoredFields: TMVCIgnoredList; const aNameCase: TMVCNameCase): IMVCRESTResponse;
var
lResource: string;
2020-08-09 02:18:36 +02:00
begin
lResource := aResource + '/' + aKeyValue;
Result := Put(lResource, fSerializer.SerializeDataSetRecord(aDataSet, aIgnoredFields, aNameCase));
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.Delete: IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Result := ExecuteRESTRequest(TRESTRequestMethod.rmDELETE);
end;
function TMVCRESTClient.Delete(const aResource: string): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Resource(aResource);
Result := Delete;
end;
destructor TMVCRESTClient.Destroy;
2013-11-11 12:23:49 +01:00
begin
fRESTResponse.Free;
fRESTRequest.Free;
fRESTClient.Free;
fSerializer := nil;
2020-08-09 02:18:36 +02:00
fRttiContext.Free;
2013-11-11 12:23:49 +01:00
inherited Destroy;
2013-10-30 00:48:23 +01:00
end;
procedure TMVCRESTClient.ExecuteAsyncRESTRequest;
2020-08-20 23:35:28 +02:00
var
lMVCRESTClient: IMVCRESTClient;
lAsyncCompletionHandler: TProc<IMVCRESTResponse>;
lAsyncSynchronized: Boolean;
lAsyncCompletionHandlerWithError: TProc<Exception>;
begin
// For asynchronous execution, a clone of RESTClient is created to be executed in an anonymous thread
lMVCRESTClient := CloneRESTClient;
lAsyncCompletionHandler := fAsyncCompletionHandler;
lAsyncSynchronized := fAsyncSynchronized;
lAsyncCompletionHandlerWithError := fAsyncCompletionHandlerWithError;
TThread.CreateAnonymousThread(
procedure
var
lMVCRESTResponse: IMVCRESTResponse;
begin
try
TMVCRESTClient(lMVCRESTClient).fRESTRequest.Execute;
lMVCRESTResponse := TMVCRESTResponse.Create(TMVCRESTClient(lMVCRESTClient).fRESTResponse);
{$IF not defined(SYDNEYORBETTER)}
TMVCRESTResponse(lMVCRESTResponse).SetCookies(TMVCRESTClient(lMVCRESTClient).GetResponseCookies);
{$ENDIF}
TMonitor.Enter(TObject(lMVCRESTResponse));
try
if Assigned(lAsyncCompletionHandler) then
begin
if lAsyncSynchronized then
TThread.Synchronize(TThread.CurrentThread,
procedure
begin
lAsyncCompletionHandler(lMVCRESTResponse)
end
)
else
lAsyncCompletionHandler(lMVCRESTResponse);
end;
finally
TMonitor.Exit(TObject(lMVCRESTResponse));
2020-08-20 23:35:28 +02:00
end;
except
on E: Exception do
begin
if Assigned(lAsyncCompletionHandlerWithError) then
begin
if fAsyncSynchronized then
TThread.Synchronize(nil,
procedure
begin
lAsyncCompletionHandlerWithError(E)
end
)
else
lAsyncCompletionHandlerWithError(E);
end;
end;
end;
end).Start;
end;
function TMVCRESTClient.ExecuteRESTRequest(const aMethod: TRESTRequestMethod): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
fRESTRequest.Method := aMethod;
if fNextRequestIsAsync then
begin
Result := nil;
ExecuteAsyncRESTRequest;
end
else
begin
fRESTRequest.Execute;
Result := TMVCRESTResponse.Create(fRESTResponse);
{$IF not defined(SYDNEYORBETTER)}
TMVCRESTResponse(Result).SetCookies(GetResponseCookies);
{$ENDIF}
ClearAllParams;
end;
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.FallbackCharsetEncoding: string;
begin
Result := fRESTClient.FallbackCharsetEncoding;
end;
function TMVCRESTClient.FallbackCharsetEncoding(const aFallbackCharsetEncoding: string): IMVCRESTClient;
begin
Result := Self;
fRESTClient.FallbackCharsetEncoding := aFallbackCharsetEncoding;
end;
function TMVCRESTClient.Get(const aResource: string): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Resource(aResource);
Result := ExecuteRESTRequest(TRESTRequestMethod.rmGET);
end;
function TMVCRESTClient.HandleRedirects(const aHandleRedirects: Boolean): IMVCRESTClient;
begin
Result := Self;
fRESTRequest.HandleRedirects := aHandleRedirects;
end;
function TMVCRESTClient.HandleRedirects: Boolean;
begin
Result := fRESTRequest.HandleRedirects;
end;
function TMVCRESTClient.HeaderValue(const aName: string): string;
var
lParam: TRESTRequestParameter;
begin
Result := '';
lParam := fRESTRequest.Params.ParameterByName(aName);
if Assigned(lParam) and (lParam.Kind = TRESTRequestParameterKind.pkHTTPHEADER) then
Result := lParam.Value;
end;
class function TMVCRESTClient.New: IMVCRESTClient;
2020-08-14 00:43:25 +02:00
begin
Result := TMVCRESTClient.Create;
2020-08-14 00:43:25 +02:00
end;
function TMVCRESTClient.ObjectIsList(aObject: TObject): Boolean;
2020-08-09 02:18:36 +02:00
begin
Result := fRttiContext.GetType(aObject.ClassType).GetMethod('GetEnumerator') <> nil;
end;
function TMVCRESTClient.Get: IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Result := ExecuteRESTRequest(TRESTRequestMethod.rmGET);
end;
function TMVCRESTClient.Patch(const aResource, aBody: string; const aDoNotEncode: Boolean;
const aContentType: TRESTContentType): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Resource(aResource);
if not aBody.isEmpty then
begin
ClearBody;
AddBody(aBody, aDoNotEncode, aContentType);
2020-08-09 02:18:36 +02:00
end;
Result := Patch;
end;
function TMVCRESTClient.Patch: IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Result := ExecuteRESTRequest(TRESTRequestMethod.rmPATCH);
end;
function TMVCRESTClient.Patch(const aResource: string; aBody: TObject; const aOwnsBody: Boolean): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
if aBody = nil then
raise EMVCRESTClientException.Create('You need a valid body!');
2020-08-09 02:18:36 +02:00
Result := Patch(aResource, SerializeObject(aBody));
if aOwnsBody then
aBody.Free;
end;
function TMVCRESTClient.Post(const aResource: string; aBody: TObject; const aOwnsBody: Boolean): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
if aBody = nil then
raise EMVCRESTClientException.Create('You need a valid body!');
2020-08-09 02:18:36 +02:00
Result := Post(aResource, SerializeObject(aBody));
if aOwnsBody then
aBody.Free;
end;
function TMVCRESTClient.Post(const aResource, aBody: string; const aDoNotEncode: Boolean;
const aContentType: TRESTContentType): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Resource(aResource);
if not aBody.IsEmpty then
begin
ClearBody;
AddBody(aBody, aDoNotEncode, aContentType);
2020-08-09 02:18:36 +02:00
end;
Result := Post;
end;
function TMVCRESTClient.Post: IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Result := ExecuteRESTRequest(TRESTRequestMethod.rmPOST);
end;
function TMVCRESTClient.ProxyPassword(const aProxyPassword: string): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
fRESTClient.ProxyPassword := aProxyPassword;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.PreEncodeURL(const aURL: string): string;
begin
// It is necessary to encode the dots because the HTTPClient removes dotted URL segments.
// See https://tools.ietf.org/html/rfc3986#section-5.2.4
Result := aURL;
Result := Result.Replace('\', '/', [rfReplaceAll]);
Result := Result.Replace('../', '%2E%2E/', [rfReplaceAll]);
Result := Result.Replace('./', '%2E/', [rfReplaceAll]);
end;
function TMVCRESTClient.ProxyPassword: string;
2015-12-18 20:59:40 +01:00
begin
Result := fRESTClient.ProxyPassword;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTClient.ProxyPort: Integer;
2013-10-30 00:48:23 +01:00
begin
Result := fRESTClient.ProxyPort;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.ProxyPort(const aProxyPort: Integer): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
fRESTClient.ProxyPort := aProxyPort;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.ProxyServer(const aProxyServer: string): IMVCRESTClient;
2015-04-01 17:01:23 +02:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
fRESTClient.ProxyServer := aProxyServer;
2015-04-01 17:01:23 +02:00
end;
function TMVCRESTClient.ProxyServer: string;
2013-10-30 00:48:23 +01:00
begin
Result := fRESTClient.ProxyServer;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.ProxyUsername: string;
2013-10-30 00:48:23 +01:00
begin
Result := fRESTClient.ProxyUsername;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.ProxyUsername(const aProxyUsername: string): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
2020-08-09 02:18:36 +02:00
fRESTClient.ProxyUsername := aProxyUsername;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.Put(const aResource: string; aBody: TObject; const aOwnsBody: Boolean): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
if aBody = nil then
raise EMVCRESTClientException.Create('You need a valid body!');
2020-08-09 02:18:36 +02:00
Result := Put(aResource, SerializeObject(aBody));
if aOwnsBody then
aBody.Free;
end;
function TMVCRESTClient.Put(const aResource, aBody: string; const aDoNotEncode: Boolean;
const aContentType: TRESTContentType): IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Resource(aResource);
if not aBody.IsEmpty then
begin
ClearBody;
AddBody(aBody, aDoNotEncode, aContentType);
2020-08-09 02:18:36 +02:00
end;
Result := Put;
end;
function TMVCRESTClient.Put: IMVCRESTResponse;
2020-08-09 02:18:36 +02:00
begin
Result := ExecuteRESTRequest(TRESTRequestMethod.rmPUT);
end;
function TMVCRESTClient.RaiseExceptionOn500(const aRaiseExceptionOn500: Boolean): IMVCRESTClient;
2020-08-09 02:18:36 +02:00
begin
Result := Self;
fRESTClient.RaiseExceptionOn500 := aRaiseExceptionOn500;
end;
function TMVCRESTClient.Resource: string;
2020-08-09 02:18:36 +02:00
begin
Result := fRESTRequest.Resource;
end;
{$IF not defined(SYDNEYORBETTER)}
function TMVCRESTClient.GetResponseCookies: TArray<TCookie>;
var
lRttiType: TRttiType;
lRttiField: TRttiField;
lRestHttp: TRESTHTTP;
lHttpResponse: IHTTPResponse;
begin
SetLength(Result, 0);
lRttiType := fRttiContext.GetType(fRESTClient.ClassType);
lRttiField := lRttiType.GetField('FHttpClient');
if not Assigned(lRttiField) then
Exit;
lRestHttp := lRttiField.GetValue(fRESTClient).AsObject as TRESTHTTP;
lRttiType := fRttiContext.GetType(lRestHttp.ClassType);
lRttiField := lRttiType.GetField('FHTTPResponse');
if not Assigned(lRttiField) then
Exit;
lHttpResponse := lRttiField.GetValue(lRestHttp).AsInterface as IHTTPResponse;
Result := lHttpResponse.Cookies.ToArray;
end;
{$ENDIF}
function TMVCRESTClient.RegisterTypeSerializer(const aTypeInfo: PTypeInfo;
2020-08-20 23:35:28 +02:00
aInstance: IMVCTypeSerializer): IMVCRESTClient;
begin
Result := Self;
fSerializer.RegisterTypeSerializer(aTypeInfo, aInstance);
end;
function TMVCRESTClient.Resource(const aResource: string): IMVCRESTClient;
2020-08-09 02:18:36 +02:00
begin
Result := Self;
fRESTRequest.Resource := PreEncodeURL(ConvertMVCPathParamsToRESTParams(aResource));
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.RaiseExceptionOn500: Boolean;
2020-08-09 02:18:36 +02:00
begin
Result := fRESTClient.RaiseExceptionOn500;
end;
function TMVCRESTClient.ReadTimeout: Integer;
begin
{$IF defined(SYDNEYORBETTER)}
Result := fRESTRequest.ReadTimeout;
{$ELSE}
Result := fRESTRequest.Timeout;
{$ENDIF}
end;
function TMVCRESTClient.ReadTimeout(const aReadTimeout: Integer): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
{$IF defined(SYDNEYORBETTER)}
fRESTRequest.ReadTimeout := aReadTimeout;
{$ELSE}
fRESTRequest.Timeout := aReadTimeout;
{$ENDIF}
2020-08-09 02:18:36 +02:00
end;
function TMVCRESTClient.URLAlreadyEncoded(const aURLAlreadyEncoded: Boolean): IMVCRESTClient;
begin
Result := Self;
fRESTRequest.URLAlreadyEncoded := aURLAlreadyEncoded;
end;
function TMVCRESTClient.UserAgent(const aUserAgent: string): IMVCRESTClient;
begin
Result := Self;
fRESTClient.UserAgent := aUserAgent;
end;
function TMVCRESTClient.UserAgent: string;
begin
Result := fRESTClient.UserAgent;
end;
2020-08-20 20:38:32 +02:00
function TMVCRESTClient.URLAlreadyEncoded: Boolean;
begin
Result := fRESTRequest.URLAlreadyEncoded;
end;
2020-08-26 22:13:18 +02:00
function TMVCRESTClient.SecureProtocols: THTTPSecureProtocols;
begin
Result := fRESTClient.SecureProtocols;
end;
function TMVCRESTClient.SecureProtocols(const aSecureProtocols: THTTPSecureProtocols): IMVCRESTClient;
begin
Result := Self;
fRESTClient.SecureProtocols := aSecureProtocols;
end;
function TMVCRESTClient.SerializeObject(aObject: TObject): string;
2020-08-09 02:18:36 +02:00
begin
if ObjectIsList(aObject) then
Result := fSerializer.SerializeCollection(aObject)
else
Result := fSerializer.SerializeObject(aObject);
end;
function TMVCRESTClient.SetBasicAuthorization(const aUsername, aPassword: string): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
var
LBase64: TNetEncoding;
LAuthValue: string;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
// Do not use TNetEncoding.Base64 here, because it may break long line
LBase64 := TBase64Encoding.Create(0, '');
try
LAuthValue := BASIC_AUTH_PREFIX + LBase64.Encode(aUsername + ':' + aPassword);
finally
LBase64.Free;
2013-10-30 00:48:23 +01:00
end;
AddHeader(AUTHORIZATION_HEADER, LAuthValue, True)
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTClient.SetBearerAuthorization(const aToken: string): IMVCRESTClient;
2013-10-30 00:48:23 +01:00
begin
Result := Self;
AddHeader(AUTHORIZATION_HEADER, BEARER_AUTH_PREFIX + aToken, True);
2013-10-30 00:48:23 +01:00
end;
{ TMVCRESTResponse }
2015-12-22 12:17:13 +01:00
function TMVCRESTResponse.Content: string;
2013-10-30 00:48:23 +01:00
begin
Result := fContent;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTResponse.ContentEncoding: string;
2015-12-18 20:59:40 +01:00
begin
Result := fContentEncoding;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTResponse.ContentLength: Integer;
2015-12-18 20:59:40 +01:00
begin
Result := fContentLength;
2015-12-18 20:59:40 +01:00
end;
function TMVCRESTResponse.ContentType: string;
2013-10-30 00:48:23 +01:00
begin
Result := fContentType;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTResponse.CookieByName(const aName: string): TCookie;
var
lCookie: TCookie;
begin
Result := Default (TCookie);
for lCookie in fCookies do
begin
if SameText(lCookie.Name, aName) then
Exit(lCookie);
end;
end;
function TMVCRESTResponse.Cookies: TCookies;
begin
Result := fCookies;
end;
constructor TMVCRESTResponse.Create(aRESTResponse: TCustomRESTResponse);
2013-10-30 00:48:23 +01:00
begin
inherited Create;
fHeaders := TStringList.Create;
SetLength(fRawBytes, 0);
fCookies := TCookies.Create;
2020-08-09 02:18:36 +02:00
FillRESTResponse(aRESTResponse);
2013-10-30 00:48:23 +01:00
end;
destructor TMVCRESTResponse.Destroy;
2015-04-01 17:01:23 +02:00
begin
SetLength(fRawBytes, 0);
fHeaders.Free;
fCookies.Free;
inherited Destroy;
2015-04-01 17:01:23 +02:00
end;
function TMVCRESTResponse.ErrorMessage: string;
2013-10-30 00:48:23 +01:00
begin
2020-08-09 02:18:36 +02:00
Result := fErrorMessage;
end;
procedure TMVCRESTResponse.FillRESTResponse(aRESTResponse: TCustomRESTResponse);
2020-08-09 02:18:36 +02:00
begin
fSuccess := aRESTResponse.Status.Success;
fStatusCode := aRESTResponse.StatusCode;
fStatusText := aRESTResponse.StatusText;
fErrorMessage := aRESTResponse.ErrorMessage;
2020-08-14 00:43:25 +02:00
fHeaders.Assign(aRESTResponse.Headers);
{$IF defined(SYDNEYORBETTER)}
fCookies.AddRange(aRESTResponse.Cookies.ToArray);
{$ENDIF}
2020-08-09 02:18:36 +02:00
fServer := aRESTResponse.Server;
fFullRequestURI := aRESTResponse.FullRequestURI;
fContent := aRESTResponse.Content;
fRawBytes := aRESTResponse.RawBytes;
fContentType := aRESTResponse.ContentType;
fContentEncoding := aRESTResponse.ContentEncoding;
fContentLength := aRESTResponse.ContentLength;
end;
function TMVCRESTResponse.FullRequestURI: string;
2020-08-09 02:18:36 +02:00
begin
Result := fFullRequestURI;
end;
function TMVCRESTResponse.HeaderValue(const aName: string): string;
2020-08-09 02:18:36 +02:00
begin
Result := fHeaders.Values[aName];
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTResponse.Headers: TStrings;
begin
Result := fHeaders;
end;
function TMVCRESTResponse.RawBytes: TBytes;
begin
Result := fRawBytes;
end;
procedure TMVCRESTResponse.SaveContentToFile(const aFileName: string);
2020-08-09 02:18:36 +02:00
var
lStream: TMemoryStream;
2018-03-10 23:50:44 +01:00
begin
2020-08-09 02:18:36 +02:00
lStream := TMemoryStream.Create;
try
lStream.Write(fRawBytes, Length(fRawBytes));
lStream.Position := 0;
lStream.SaveToFile(aFileName);
finally
lStream.Free;
end;
2018-03-10 23:50:44 +01:00
end;
procedure TMVCRESTResponse.SaveContentToStream(aStream: TStream);
2018-03-10 23:50:44 +01:00
begin
2020-08-09 02:18:36 +02:00
if aStream = nil then
raise EMVCRESTClientException.Create('Stream not assigned!');
2020-08-09 02:18:36 +02:00
aStream.Write(fRawBytes, Length(fRawBytes));
end;
function TMVCRESTResponse.Server: string;
2020-08-09 02:18:36 +02:00
begin
Result := fServer;
2018-03-10 23:50:44 +01:00
end;
{$IF not defined(SYDNEYORBETTER)}
procedure TMVCRESTResponse.SetCookies(aCookies: TArray<TCookie>);
var
i: Integer;
lCookies: string;
begin
fCookies.AddRange(aCookies);
if (fHeaders.IndexOfName(HEADER_RESPONSE_COOKIES) = - 1) and (fCookies.Count > 0) then
begin
lCookies := '';
for i := 0 to fCookies.Count - 1 do
lCookies := lCookies + '; ' + fCookies[i].ToString;
fHeaders.Add(HEADER_RESPONSE_COOKIES + '=' + lCookies.Substring(2));
end;
end;
{$ENDIF}
function TMVCRESTResponse.StatusCode: Integer;
2013-10-30 00:48:23 +01:00
begin
Result := fStatusCode;
2013-10-30 00:48:23 +01:00
end;
function TMVCRESTResponse.StatusText: string;
2013-11-05 14:57:50 +01:00
begin
Result := fStatusText;
2013-11-05 14:57:50 +01:00
end;
function TMVCRESTResponse.Success: Boolean;
2013-10-30 00:48:23 +01:00
begin
Result := fSuccess;
2013-10-30 00:48:23 +01:00
end;
{ THackCustomRESTRequest }
procedure THackCustomRESTRequest.DoApplyURLSegments(const aParamList: TRESTRequestParameterArray; var aURL: string);
var
lParameter: TRESTRequestParameter;
lReplace: string;
lEncodedParam: string;
begin
for lParameter in aParamList do
if lParameter.Kind = TRESTRequestParameterKind.pkURLSEGMENT then
begin
lReplace := '{' + lParameter.Name + '}';
lEncodedParam := TNetEncoding.URL.Encode(lParameter.Value, PATH_UNSAFE_CHARS,
[TURLEncoding.TEncodeOption.EncodePercent]);
aURL := StringReplace(aURL, lReplace, lEncodedParam, [rfReplaceAll, rfIgnoreCase]);
end;
end;
2013-10-30 00:48:23 +01:00
end.