Does anyone know how to process 404 page contents using #Indy ? After receiving the 404 response, I can't access the Contentstream.
Does anyone know how to process 404 page contents using #Indy ? After receiving the 404 response, I can't access the Contentstream.
Response.ContentLength <> Response.ContentStream.Size
Response.ContentLength <> Response.ContentStream.Size
Not sure. The last time I did HTTP with Delphi was using these wrappers, but not with any negative error handling as it was proof of concept state: https://bitbucket.org/jeroenp/besharp.net/commits/0b051f59a2834c5ab5280bcbad23c26203eccfab (final implementation was C# because of various reasons).
ReplyDeleteYes, but don't have code to hand. Will try to get back to you...
ReplyDeleteWalter Prins that would be great. The only ugly solution I found so far is to catch EIdHTTPProtocolException where Errormessage contains the response pages body.
ReplyDeleteThe problem is that I'm trying to consume a REST/JSON service, where the body can contain different errorcodes, e.g.:
{
"errorCode" : 404,
"errorDescription" : "Resource not found."
}
Oliver Funcke that was the part of the reason to move this piece to C#. JSON is a piece of cake there and it just works.
ReplyDeleteJeroen Wiert Pluimers C# is no option in this case.
ReplyDeleteThere are alternatives to Indy, e.g. the new HTTP client introduced with XE8, or classes, like our Open source SynCrtSock.pas unit, which allows raw socket, WinHttp or WinINet access. https://github.com/synopse/mORMot/blob/master/SynCrtSock.pas
ReplyDeleteAlso consider alternatives for JSON process, if you are using the DBXJson.pas official unit, which is just slow and difficult to work with.
Sorry I finally had a look at the code I was thinking of. I'd forgotten about the irritating way Indy handled HTTP non-success response codes...
ReplyDeleteBut to answer your question, basically in the case of error, you can get what would've normally been in the contentstream from the ExceptionObj.ErrorMessage property. So you can use something like the following if you want to get the content response regardless of http response code (untested):
var
FResponseStream: TStringStream;
FRequestURL, Content : String;
begin
//.... etc etc
try
FIdHTTP.Get(FRequestURL, FResponseStream);
Content := FResponseStream.DataString;
except
on E:EIdHTTPProtocolException do
Content := E.ErrorMessage;
end;
// At this point, "Content" contains the response body, both for
// successful (200) as well as other response codes.
//.... etc etc
end;
Please note, the above obviously squashes the error/error code details/exception and is therefore likely not actually how you'd want to do it in reality - presumably interpreting a (JSON?) error body would be handled by different code in the client from the "200 success" happy path -- in that case re-raising the exception, or raising a different "local" exception that signals that something went wrong and that would be caught by some suitable handling code somewhere else would be one way to deal with it.
Let me know if this doesn't help and/or I've misunderstood your problem.
Walter Prins thanks, that was the solution I mentioned as ugly in my follow-up post.
ReplyDeletetry
FResponse := FClient.Get(FURI);
Result := True;
except
on E: EIdHTTPProtocolException do
begin
FResponse := EIdHTTPProtocolException(E).ErrorMessage;
end;
end;
hoNoProtocolErrorException and array of error codes do basically the same. No exception is raised and the input stream gets lost in case of 404. The only way really seems to let the exception raise and get the data from its errormessage field.
ReplyDeleteMust say that behaviour seems kind of illogical. If one's explicitly telling Indy to NOT raise an exception, then you'd expect Indy to then just return everything retrieved, including content... As-is, hoNoProtocolErrorException & friends usefulness seems rather limited then -- I wonder if this behaviour is really intentional or whether this is worth a bug report?
ReplyDeleteIt's a problem whitin the ProcessResponse / Checkexception, setting the Response.ContentStream back to the state before it had been read.
ReplyDelete