skip to Main Content

I am trying to get my RESTClient’s CRUD Requests to work after retrieving the access-token from Azure but I don’t know what parameters need to be added and I cannot find examples of the syntax that work, or maybe my app’s registration info isn’t correct for a REST API. Both apps are written Delphi 10.4.2 (Sydney).

I have a RESTServer/RESTClient pair of apps (originated using the IDE’s DataSnap wizards). I added a RESTful WebAction to the RESTServer and a corresponding GET Request to the RESTClient, and they perform perfectly if both apps reside on my network or both are on a drive in my Azure Windows Server VM. I wish to have only the RESTServer app on Azure so I am trying to get my RESTClient app to connect to the RESTServer app on Azure Windows Server VM.

I put a TOAuth2Authenticator component on a form in my Delphi 10.4.2 app and was able to confirm that I can retrieve the Access-Token using the identity values from Azure app registration info. The registration specifies that this is a "Public" API and I created a Scope for it but I’m not sure a Scope is needed for a Public REST API.

If I ping the Public IP of the VM I get "Request timed out." but I’m assuming that is normal and retrieval of the token works anyway, which is confirmed in the following code from this test app:

procedure TMainForm.RESTTokenRequestAfterExecute(Sender: TCustomRESTRequest);
begin
if RESTTokenResponse.StatusCode = 200 then
  begin
  MyAuthToken := RESTTokenResponse.JSONValue.GetValue<String>('access_token');
  OAuth2Authenticator5.AccessToken := FConnection.MyAuthToken; // store it here in case I decide to use this component.

That code and the RESTClient code to retrieve the token are based on this demo app:
https://github.com/jfbilodeau/AzureRESTExample

But the following code (and dozens of variations) in my app always times out with
ENetHTTPClientException with message ‘Error sending data: (12002) The operation timed out’.
ERESTException with message ‘REST request failed: Error sending data: (12002) The operation timed out’.

I’ve read messages that suggest that the Resource parameter is not used now, at least not for Public APIs, and I don’t see any examples about how to specify the Scope in Delphi components.

Intuitively, it would make sense to apply a "REST Endpoint" URL to these Requests that use the Token for subsequent contact.

Is all of that handled by the contents of the Token? I have parsed the Token at jwt.io and cannot tell if it contains all that is needed for the connection. I could redact some of that and include it if that would be helpful.

procedure MyForm.Button1Click(Sender: TObject);
var
  RESTClient1: TRESTClient;
  RESTRequest1: TRESTRequest;
  strImageJSON : string;
begin
  RESTClient1 := TRESTClient.Create('http://<Azure Public IP>:8080/'); // tried https also
  try
    RESTRequest1 := TRESTRequest.Create(nil);
    try
      RESTRequest1.Method := TRESTRequestMethod.rmGET; // or rmPOST?
      // MyAzureResource from Delphi RESTServer app's WebModule.WebActionItem's Pathinfo
      RESTRequest1.Resource := 'api/<AzureClientID>/<MyAzureResource>'; 
      RESTRequest1.Params.AddItem('MyKey1', 'MyValue1', TRESTRequestParameterKind.pkREQUESTBODY);
      RESTRequest1.Params.AddItem('MyKey2', 'MyValue2', TRESTRequestParameterKind.pkREQUESTBODY);
      RESTRequest1.Params.AddItem('Authorization','Bearer ' + FConnection.AuthToken);
      RESTRequest1.Client := RESTClient1;
      RESTRequest1.Execute;
      strImageJSON := RESTRequest1.Response.Content;
    finally
      RESTRequest1.Free;
    end;
  finally
    RESTClient1.Free;
  end;
end;

When not connecting to Azure, the basic Request with only these 2 parameters works fine:

RESTRequest1.Params.AddItem(‘MyKey1’, ‘MyValue1’);
RESTRequest1.Params.AddItem(‘MyKey2’, ‘MyValue2’);

Can you tell me what is wrong with this Request under Azure?

2

Answers


  1. The problem is that you’re passing the authorization bearer token as parameter and not as an header try this code

    RESTRequest1.Params.AddHeader('Authorization','Bearer ' + FConnection.AuthToken);
    

    Sending Authorization Bearer Token Header. To send a request with the
    Bearer Token authorization header, you need to make an HTTP request
    and provide your Bearer Token in the "Authorization: Bearer {token}"
    HTTP header. A Bearer Token is a cryptic string typically generated by
    the server in response to a login request

    https://reqbin.com/req/adf8b77i/authorization-bearer-header#:~:text=Sending%20Authorization%20Bearer%20Token%20Header,response%20to%20a%20login%20request.

    in your code you where sending the authorization as a request parameter

    btw if i can give you a suggestion try to use indy to make rest call is pretty easy and you can handle everything IMO

    Login or Signup to reply.
  2. This is an example of your call made with indy10 on delphi 11

        procedure TForm1.pippo;
        var
        fIdHTTP: TIdHTTP;  //unit IdHTTP
        fIdSSLIOHandlerSocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL; //unit IdSSLOpenSSL
        responseCall, url: string;
        responseCodeCall: Integer;
        StringStream: TStringStream;
        data : TJSONObject; //unit System.Json
        begin
    
    
          //set the ssl versione
          fIdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
          with fIdSSLIOHandlerSocketOpenSSL.SSLOptions do begin
            Method := sslvTLSv1_2;
            Mode   := sslmClient;
            SSLVersions := [sslvTLSv1_2];
          end;
    
          //create the object that will do the call
          fIdHTTP := TIdHTTP.Create(nil);
    
          //assign the ssl option to your idhttp object
          fIdHTTP.IOHandler := fIdSSLIOHandlerSocketOpenSSL;
    
          //set foldlines property of custom headers at false then with the add pair
          //add your bearer token
          fIdHTTP.Request.CustomHeaders.FoldLines := false;
          fIdHTTP.Request.CustomHeaders.AddPair('Authorization','Bearer ' + '<token>');
    
          //i like to set set the default responde of the call to error
          responseCall :=  '';
          responseCodeCall := 500;
    
          //Create the object that your passing
          data := TJSONObject.Create;
          data.AddPair('MyKey1', 'MyValue1');
          data.AddPair('MyKey2', 'MyValue2');
          // this code / it's equal to create a string like this / and then passit to the call instead of data.ToJson
          //    {
          //        "MyKey1": "MyValue1",
          //        "MyKey2": "MyValue2",
          //    }
    
          StringStream := nil;
    
          url := 'http://<Azure Public IP>:8080/api/<AzureClientID>/<MyAzureResource>';;
    
          try
            try
    
              //transform the body of your request (the json) into a stream
              StringStream := TStringStream.Create(data.ToJSON, TEncoding.UTF8);
              //By the fact that you're sending a body i assume it's not a get but a post
              //but better check the specification of the api
              //Make a post passing the stream of the body
              responseCall := fIdHTTP.Post(url,StringStream);
    
              //get the response of the call
              responseCodeCall := fIdHTTP.ResponseCode;
    
            except  //All error handle
              on e1: EIdHTTPProtocolException do
              begin
                responseCodeCall := fIdHTTP.ResponseCode;
                responseCall := e1.ErrorMessage;
              end;
              on E: Exception do
              begin
                responseCodeCall := 500;
                responseCall := e.Message;
              end;
            end;
    
          finally
    
            if StringStream <> nil then
              StringStream.Free;
    
            data.free;
    
            fIdSSLIOHandlerSocketOpenSSL.Free;
            fIdHTTP.Free;
    
          end;
    
    
          if (responseCodeCall >= 200) and (responseCodeCall <= 299) then
            //Great success
          else
            //Great unseccess :(
    
        end;
    

    it should work fine on Sydney version

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search