6

Is there a way to prevent Indy from raising exceptions on any/all HTTP statuses?

My understanding of the IgnoreReplies array is that it'll prevent those statuses from ever coming back, but that's not what I want. I want ALL statuses coming back, and none of them to raise an exception. There are plenty of REST services that return 404 for example, and that's considered a perfectly "valid" response.

I really don't want 1/2 of my code in exception handlers, so is there way to get the current Indy to just return everything?

TLama
  • 75,147
  • 17
  • 214
  • 392
DaveS_Lifeway
  • 375
  • 3
  • 9

1 Answers1

12

Sorry, but you have to use the IgnoreReplies parameter to tell TIdHTTP which specific HTTP reply codes to not raise an exception for. There is currently no way to tell TIdHTTP to not raise an exception for all possible replies generically. If you want to receive a non-success reply, like 404, without raising an exception then you have to tell TIdHTTP that, eg:

IdHTTP.Get('http://host/path', [404]);

Note that this syntax is only available for GET requests, not other requests, like POST (unless you call the protected TIdHTTP.DoRequest() method directly).

Indy is specifically designed around exception handling. You have to embrace exceptions, not avoid them, if you want to use Indy effectively. What is so troublesome about wrapping TIdHTTP.Get() or TIdHTTP.Post() in a small try/except block? If you don't want to handle all possible exceptions, then you can have the except block handle EIdHTTPProtocolException by itself.

try
  IdHTTP.Get('http://host/path');
except
  on E: EIdHTTPProtocolException do
  begin
    if E.ErrorCode <> 404 then Raise;
  end;
end;

Update: thinking about it more, it might be worth adding a new flag to the TIdHTTP.HTTPOptions property to disable the exception for all status codes. I'm considering it.

Update: a new hoNoProtocolErrorException flag has now been added to the TIdHTTP.HTTPOptions property:

IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoNoProtocolErrorException];
IdHTTP.Get('http://host/path');
// use IdHTTP.ResponseCode as needed...

Update: in addition, there is also a hoWantProtocolErrorContent flag as well. When hoNoProtocolErrorException is enabled and an HTTP error occurs, if hoWantProtocolErrorContent is enabled then the error content will be returned to the caller (either in a String return value or in an AResponseContent output stream, depending on which version of Get()/Post() is being called), otherwise the content will be discarded.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 3
    "You have to embrace exceptions, not avoid them, if you want to use Indy effectively" unfortunately yes. Why it is troublesome? Because raising and handling exceptions should not be part of normal program flow. Receiving a 404 in OP's context is normal program flow, not an exceptional situation. Also troublesome because (on Win32) raising and handling exceptions hits performance pretty hard. – Marjan Venema Feb 22 '13 at 15:37
  • Marking this as the accepted answer. Remy, thanks for considering/changing this. As Marjan alluded to, having one case statement in one place, and another in the except block, would make for pretty ugly code - this addition will solve that perfectly. Thanks again! – DaveS_Lifeway Feb 22 '13 at 17:24
  • 1
    No, `IgnoreReplies` is only available for `Get()` and `DoRequest()` at this time. But the `hoNoProtocolErrorException` flag in the `TIdHTTP.HTTPOptions` property can be used with `Post()`. – Remy Lebeau May 06 '15 at 02:01
  • As can the `hoWantProtocolErrorContent` flag. – Remy Lebeau Dec 07 '16 at 19:20
  • I do have `hoNoProtocolErrorException` available in my Delphi 10 Seattle, but there is no `hoWantProtocolErrorContent`... My problem is that the server I work with responds with content when there's a protocol error. For example, `500 Internal Server Error`, and I'm expected to read the content to see the error. `hoNoProtocolErrorException` does suppress the exception, but the response content stream comes empty when using POST. – Jerry Dodge Apr 02 '18 at 14:14
  • 1
    @JerryDodge `hoWantProtocolErrorContent` was [added over 2 years ago](http://www.indyproject.org/Sockets/Blogs/ChangeLog/20160110.aspx). You will have to either [update your version of Indy](http://www.indyproject.org/Sockets/Docs/Indy10Installation.aspx), or handle the `EIdHTTPProtocolException` which contains the error content in its `ErrorMessage` property – Remy Lebeau Apr 02 '18 at 14:56
  • @Remy Thanks for your speedy response - Updating Indy is a bit out of the question, so I'm trying to see about using the exception's `ErrorMessage` - it appears to be something internally generated (HTML). But the server is sending JSON on the response stream - I just need that JSON. Should this be a new Q? – Jerry Dodge Apr 02 '18 at 15:46
  • @JerryDodge The `EIdHTTPProtocolException.ErrorMessage` will contain whatever content the server actually sends. If you are expecting JSON but are receiving HTML, then the server is not sending what you are expecting. Indy does not generate its own content for the `ErrorMessage` – Remy Lebeau Apr 02 '18 at 17:22
  • @Remy You commented seconds before posting a new question. It appears actually that the third party REST server (which is actually `TIdHTTPServer`) is internally generating this. However when I test in Postman I get JSON. I even changed the `Accepts`. So I will check with the server's author. Thank you. – Jerry Dodge Apr 02 '18 at 17:35
  • @JerryDodge then the 3rd party is likely not using `TIdHTTPServer` correctly. The only time it ever generates its own HTML is when sending a response without any `ContentText` or `ContentStream` assigned to the `TIdHTTPResponse` object. It is fairly easy to get the raw HTTP request sent by Postman and compare it to the raw HTTP request sent by `TIdHTTP`, so you should do that and compare them for differences – Remy Lebeau Apr 02 '18 at 17:56
  • @RemyLebeau, any chance that the IgnoreReplies parameter will be added to the other HTTP verbs; POST, PUT, PATCH, etc.? The REST client library I use in turn uses idHttp and expects the content from the server to be on the response stream. – TomC Jun 14 '18 at 20:38
  • @TomC probably not anytime soon, if ever (there is nothing stopping you from calling the protected `TIdHTTP.DoRequest()` method directly, which has an `AIgnoreReplies` parameter). However, even though `IgnoreReplies` will prevent an exception for *specific* error codes, it will also discard their content by default, unless `hoWantProtocolErrorContent` is enabled. So you may as well just enable `hoNoProtocolErrorException` as well. – Remy Lebeau Jun 14 '18 at 20:43
  • @RemyLebeau, unfortunately I tried doing just that and the response stream wasn't where my REST client library was expecting it to be which resulted in not deserializing the JSON payload from the server. I have already tried calling the `DoRequest()` method directly along with having enabled the `hoWantProtocolErrorContent` flag and passing the Http Status code to ignore and that works perfectly. However, I was hoping to avoid having to build a one-off copy of the Indy framework for my project just so that I can add IgnoreReplies on the other verbs. – TomC Jun 14 '18 at 21:12
  • @TomC As far as `hoWantProtocolErrorContent` is concerned, there is NO DIFFERENCE whether you use `hoNoProtocolErrorException` or `IgnoreReplies`. When an HTTP error occurs, if an `EIdHTTPProtocolException` gets raised, the content is always captured in its `ErrorMessage` property, not in the caller's output Stream. Otherwise, the content is saved to the caller's output Stream only if `hoWantProtocolErrorContent` is enabled. Otherwise the content is discarded. Look at the implementation of `CheckException()` in `IdHTTP.pas` for yourself. I assure you, it works fine, I've tested it – Remy Lebeau Jun 15 '18 at 00:08
  • @TomC all of the other verbs ultimately call `DoRequest()` internally, so there is no need to make a one-off copy of Indy just to call `DoRequest()` with `IgnoreReplies` values passed to it – Remy Lebeau Jun 15 '18 at 00:10
  • @RemyLebeau Thank you for talking me off the cliff. Not sure what I was doing wrong but your are correct. Works like a champ. Thank you! – TomC Jun 18 '18 at 15:53