2

I need to check specific status codes from a RestResponse however the RestRequest.Execute seems to generate a EHTTPProtocolException when the status codes are not 200.

I did take a look at the documentation and it states that The request execution does not raise HTTP protocol exceptions. To verify the response status, check the TCustomRESTResponse.StatusCode property.

I'm having some trouble understanding why a EHTTPProtocolException gets raised. How can I ignore this or suppress it? I did check if it's just the debugger but the code stops at execute even in a Release build and when running standalone.

Exception Screenshot

My code is as follows:

procedure TdmWebsockets.GetRustMapURL(const Connection: TsgcWSConnection; const Data: string);
begin
  var jdata := TJSONObject.ParseJSONValue(Data);
  var restClient := TRESTClient.Create(Self);
  var restResponse := TRESTResponse.Create(Self);
  var restRequest := TRESTRequest.Create(Self);
  try
    var isStaging := jdata.GetValue<Boolean>('data.staging');
    var isBarren := jdata.GetValue<Boolean>('data.barren');
    var seed := jdata.GetValue<Int32>('data.seed');
    var size := jdata.GetValue<Int32>('data.size');

    restRequest.Client := restClient;
    restRequest.Response := restResponse;

    restClient.BaseURL := 'https://rustmaps.com/api/v2/maps/' + seed.ToString + '/' + size.ToString;

    restClient.RaiseExceptionOn500 := False;

    restRequest.AddParameter('staging', BoolToStr(isStaging, True));
    restRequest.AddParameter('barren', BoolToStr(isBarren, True));

    restRequest.Params.AddHeader('X-API-Key', 'REDACTED');

    restRequest.Method := TRESTRequestMethod.rmPOST;

    Writeln('before');
    restRequest.Execute;
    Writeln('after');

    var Writer: TJsonTextWriter;
    var StringWriter: TStringWriter;
    try
      StringWriter := TStringWriter.Create();
      Writer := TJsonTextWriter.Create(StringWriter);
      Writer.Formatting := TJsonFormatting.Indented;

      Writer.WriteStartObject;
      /////////////////////////////////////////////
      Writer.WritePropertyName('command');
      Writer.WriteValue(COMMAND_GETMAP);
      /////////////////////////////////////////////
      if restResponse.Status.SuccessOK_200 or restResponse.Status.ClientErrorDuplicate_409 then
      begin
        Writeln('200 ok');

        Writer.WritePropertyName('success');
        Writer.WriteValue(True);
      /////////////////////////////////////////////
        var mapID := restResponse.JSONValue.GetValue<string>('mapId');

        Writer.WritePropertyName('mapID');
        Writer.WriteValue(mapID);
      /////////////////////////////////////////////
      end
      else if restResponse.Status.ClientErrorBadRequest_400 then
      begin
        Writeln('400 ok');
      /////////////////////////////////////////////
        Writer.WritePropertyName('success');
        Writer.WriteValue(False);
      /////////////////////////////////////////////
        var reason := restResponse.JSONValue.GetValue<string>('reason');

        Writer.WritePropertyName('reason');
        Writer.WriteValue(reason);
      /////////////////////////////////////////////
      end;

      Writer.WriteEndObject;

      Writeln(StringWriter.ToString);

      Connection.WriteData(StringWriter.ToString);
    finally
      StringWriter.Free;
      Writer.Free;
    end;
  finally
    restRequest.Free;
    restResponse.Free;
    restClient.Free;
    jdata.Free;
  end;
end;

EDIT: When clicking on the "Break" button on the exception window it takes me to the Execute method in Rest.HttpClient despite it not being added to the uses. Stepping it shows that it's executing in Rest.HttpClient and not in Rest.Client.

When trying to use try .. except I am required to Rest.HttpClient to my uses to get the exception type EHTTPProtocolException. When using try .. except it still throws out the exception. I understand the debugger will still catch it but the code never runs in the try .. except and just stops after the exception thows.

Adriaan
  • 806
  • 7
  • 24
  • @RemyLebeauI have edited my question to show that I have tried this as well and added a better explanation of whats going on. – Adriaan Mar 30 '21 at 18:57

2 Answers2

3

IF the exception is escaping TRestRequest.Execute() into your code, then just catch it with a try..except. EHTTPProtocolException has an ErrorCode property you can look at, if needed.

A try..except does not prevent an exception from being raised at all. The debugger will still show the exception being raised, unless you configure the debugger to ignore it. But your code will be able to catch the exception and process it as needed. If the debugger breaks on the exception, simply press the "Run" button on the IDE, or F9 on the keyboard, to continue execution, and the exception will be passed to the nearest matching exception handler in your code.

For example:

try
  restRequest.Execute;
except
  // swallow ALL exceptions...
end;

And just move on, no need to update the uses clause - unless you want the code to ignore non-EHTTPProtocolException exceptions and let them propagate up the call stack (as you should), in which case you can use this instead:

uses
  ..., REST.HttpClient;

try
  restRequest.Execute;
except
  on E: Exception do begin
    if not (E is EHTTPProtocolException) then begin
      // DON'T swallow unknown non-HTTP errors...
      raise;
    end;
    // swallow HTTP errors and let subsequent code check the status...
  end;
end;

Or:

uses
  ..., REST.HttpClient;

try
  restRequest.Execute;
except
  on E: EHTTPProtocolException do begin
    // swallow HTTP errors and let subsequent code check the status...
  end;
  on E: Exception do begin
    // DON'T swallow unknown non-HTTP errors...
    raise;
  end;
end;

Or:

uses
  ..., REST.HttpClient;

try
  restRequest.Execute;
except
  on E: EHTTPProtocolException do;
  // swallow only HTTP errors and let subsequent code check the status...
end;

However, IF the exception is not escaping TRestRequest.Execute() into your code, then the issue is moot. The exception will be handled inside of Execute(), no need for a try..except in your code at all. If you don't want to see the exception while debugging your code, then simply configure the debugger to ignore EHTTPProtocolException and move on.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I believe in this case the developer is expecting no exception during debugging. There will still be an EHTTPProtocolException raised, but this is trapped in REST.Client.TCustomRESTRequest.Execute method where EHTTPProtocolException is handled (E.ErrorCode >=500) and Client.RaiseExceptionsOn500. I typically set the IDE to ignore EHTTPProtocolException for this very reason. – Randy Sill Mar 30 '21 at 19:39
  • @RandySill Good point. I have updated my answer. – Remy Lebeau Mar 30 '21 at 19:50
  • Unfortunately this was not the case. What ended up happening was the thread this was running in was exiting right after "HandleEvent" got executed in Rest.Client. – Adriaan Mar 31 '21 at 12:58
  • It's probably important to note that `Execute()` does **not** raise exceptions according to [Embarcadero's Docs](https://docwiki.embarcadero.com/Libraries/Sydney/en/API:REST.Client.TCustomRESTRequest.Execute). This tripped me up for more than two hours! – Spastika Mar 09 '22 at 13:45
0

I misunderstood what the issue was and was focused on the HTTPException being thrown instead of the real issue. It seems that since my application is a console app and the NotifyEvent is noSync for the thread in sgcWebsockets the thread ended up exiting every time TCustomRESTClient.HandleEvent was called. In this case it got called and actually went through as restRequest.SynchronizedEvents is set to true by default but should be false.

So the solution was to simply do:

restRequest.SynchronizedEvents := False;
Adriaan
  • 806
  • 7
  • 24