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.
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.
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.
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;
Related
When calling one of our Internal servers with SSL URL, TRESTClient and TRESTRequest gives and error
first from Exception Class ENetHTTPCertificateException and then from ERESTException with the message 'Unspecified certificate from client'
I know that WinInet Api is being used behind, but here is no native way in the Delphi Rest Library to set the connection to Ignore Certificate Errors
- How can I code that ?
or
Does this mean the server is requesting a client certificate ?
function TdmoRestApi.GetSaLogin(var sErrorText: String): TLoginSvar;
var
LoginRoot: TLoginRootClass;
begin
Result := lsErrVilla;
RESTRequestLogin.Params.ParameterByName('TOKEN').value := FLoginToken;
RESTRequestLogin.Params.ParameterByName('X-CSRF-Token').value := 'Fetch';
try
RESTRequestLogin.Execute; //This call fails
except
on e: Exception do begin
sErrorText := e.ClassName + ' ' + e.message;
if RESTResponseLogin.StatusCode = 0 then
Exit(lsErrVilla);
end;
end;
case RESTResponseLogin.StatusCode of
200: begin
....
400: begin
....
end;
else begin
....
end;
FCsrfToken := RESTResponseLogin.Headers.Values['X-CSRF-Token'];
FLoginSvar := Result;
end;
I found the solution to your problem. I had the same problem, but manage to ignore the certificate validation with this:
In the RESTClient1 component, set the following flag to false inside the event:
procedure TForm1.RESTClient1AuthEvent(const Sender: TObject; AnAuthTarget:
AuthTargetType; const ARealm, AURL: string; var AUserName, APassword:
string; ar AbortAuth: Boolean; var Persistence: TAuthPersistenceType);
begin
AbortAuth = True;
end;
I build a datasnap REST application and I can't close the session.
For example if I call my URL 4 times in my web browser:
http://127.0.0.1:4445/REST/image?json=false
There is 4 sessions opened.
Here is my function:
function TV1.TestImage:TStream;
var
ContentType : String;
FileStream : TFileStream;
begin
FileStream := TFileStream.Create('C:\santiago.jpg', fmOpenRead or fmShareDenyNone);
ContentType := 'image/jpg';
GetInvocationMetadata.ResponseContentType := ContentType;
ContentTypeHeaderToUse := ContentType;
GetInvocationMetadata().ResponseCode := 200;
result := FileStream;
//Can't call CloseSession with TStream, pointer exception
//GetInvocationMetaData.CloseSession := True;
end;
And I use the WebModuleAfterDispatch like that:
procedure TWebModule2.WebModuleAfterDispatch(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var session : TDSSession;
begin
response.FreeContentStream := true;
if ContentTypeHeaderToUse<>'' then begin
Response.ContentType := ContentTypeHeaderToUse;
ContentTypeHeaderToUse := ''; // Reset global variable
end;
end;
As you can see, GetInvocationMetaData.CloseSession := True; can't be used here.
Do you know how to close session after sending the TStream?
On a REST server I prefer to return a base64 string, it's easier to deal for the client app than the JSON representation of a Delphi TStream (an array of bytes).
The EncodeBase64 function on the Soap.EncdDecd unit does the transformation very easily :
function TV1.TestImage:string;
var MemoryStream: TMemoryStream;
begin
MemoryStream := TMemoryStream.Create;
MemoryStream.LoadFromFile('C:\santiago.jpg');
Result := EncodeBase64(MemoryStream.Memory, MemoryStream.Size);
MemoryStream.Free; // Now you can free the Stream yourself
end;
An additional benefit is that you can now release your Stream and not depend on Datasnap doing it. So you should be able to manually close your connection without that pointer exception and solve the problem of multiple sessions remaining open.
You can directly use that base64 image string on your web application. You only need to add their type at the start of an < img > tag.
Example :
<img src="data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEVBMTczNDg3QzA5MTFFNjk3ODM5NjQyRjE2RjA3QTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEVBMTczNDk3QzA5MTFFNjk3ODM5NjQyRjE2RjA3QTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRUExNzM0NjdDMDkxMUU2OTc4Mzk2NDJGMTZGMDdBOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRUExNzM0NzdDMDkxMUU2OTc4Mzk2NDJGMTZGMDdBOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PjjUmssAAAGASURBVHjatJaxTsMwEIbpIzDA6FaMMPYJkDKzVYU+QFeEGPIKfYU8AETkCYI6wANkZQwIKRNDB1hA0Jrf0rk6WXZ8BvWkb4kv99vn89kDrfVexBSYgVNwDA7AN+jAK3gEd+AlGMGIBFDgFvzouK3JV/lihQTOwLtOtw9wIRG5pJn91Tbgqk9kSk7GViADrTD4HCyZ0NQnomi51sb0fUyCMQEbp2WpU67IjfNjwcYyoUDhjJVcZBjYBy40j4wXgaobWoe8Z6Y80CJBwFpunepIzt2AUgFjtXXshNXjVmMh+K+zzp/CMs0CqeuzrxSRpbOKfdCkiMTS1VBQ41uxMyQR2qbrXiiwYN3ACh1FDmsdK2Eu4J6Tlo31dYVtCY88h5ELZIJJ+IRMzBHfyJINrigNkt5VsRiub9nXICdsYyVd2NcVvA3ScE5t2rb5JuEeyZnAhmLt9NK63vX1O5Pe8XaPSuGq1uTrfUgMEp9EJ+CQvr+BJ/AAKvAcCiAR+bf9CjAAluzmdX4AEIIAAAAASUVORK5CYII=">
I am trying to write an HTTP server in Go for school. I am required to AVOID any libraries that make this task easy (the net/http for example).
My problem is that I cannot seem to display the HTTP Response headers correctly. I want to print each header to my terminal, line by line.
I have written this program in Java, and it works well, however, I would like to have this working with a Go program.
I have a function called 'handleClient' that takes an accepted socket.
func handleClient(c net.Conn) {
defer c.Close()
req, _ := bufio.NewReader(c).ReadString('\n')
fmt.Print(string(req))
When using a web browser to connect to 'localhost:8080', my terminal displays "GET / HTTP/1.1", which is correct, however, I need the additional lines to be posted as well. I understand that 'ReadString('\n') is what is stopping this from happening, however, it is the only way I know of to end that line.
How to I start additional lines?
You can call ReadString inside a loop until you hit EOF.
Something like this:
func handleClient(c net.Conn) {
defer c.Close()
r := bufio.NewReader(c)
for {
req, err := r.ReadString('\n')
if err != nil && err != io.EOF {
panic(err)
}
fmt.Println(req)
// break the loop if the err is eof
if err == io.EOF {
break
}
}
mkopriva pointed me in the right direction, however, EOF is not the correct way to break the loop. I need to stop looping when I run into a line that is blank. Technically it is never EOF.
To do this, I have adjusted the if condition to break the loop to the following:
if len(req) <=2 {
After all the headers are read / printed, an HTTP request will end with "\r\n" I believe. I can confirm that the last line is intact of length 2.
Thanks for the help!
I'm simply unable to retrieve the error code number when I get an error in postgres.
In the test of my program I know I'll get the following error
" pq: duplicate key value violates unique constraint "associations_pkey"".
Looking in the postgres docs this is most likely an pq error code of 23505.
I need to get that number in my Go program so that I can check on different types of errors and respond to the end user in a helpful way.
However, I can't seem to get hold of the error code in Go, only the error message. My code is as follows:
stmt, _ := DB.Prepare("INSERT INTO table (column_1) VALUES ($1)")
_, err = stmt.Exec("12324354")
if err != nil {
log.Println("Failed to stmt .Exec while trying to insert new association")
log.Println(err.Error())
fmt.Println(err.Code())
} else {
Render.JSON(w, 200, "New row was created succesfully")
}
You need to type assert the error to the type *pq.Error:
pqErr := err.(*pq.Error)
log.Println(pqErr.Code)
This is written in the documentation. As you see you can extract it in this way:
if err, ok := err.(*pq.Error); ok {
fmt.Println(err.Code)
}
Do not forget to remove the underscore from your import _ "github.com/lib/pq". As you see err has a lot of information about the error (not only Code but many others).
Notice that you can't compare it directly to some code (it is of ErrorCode type).
So you have to convert it to string and compare against a string.
https://godoc.org/github.com/lib/pq#Error
I have created a SOAPServerwithINDYVCL in Delphi XE. When a clients gets disconnected from server due to any reason like timeout at client side or net connection dropped.
I am getting error at server side as Socket Error 10053 - software caused connection abort.
this error popsup and blocking my thread of server.
I also checked this withe the demo of SOAPDMServerIndy. Here also if i start server, connect client and before server responds disconnect the client I get socket error.
Anyone knows how to handle this exception?
Even at this time this issue still exists. It's because of this code:
procedure TWebRequestHandler.HandleException(Sender: TObject);
var
Handled: Boolean;
Intf: IWebExceptionHandler;
begin
Handled := False;
if ExceptObject is Exception and
Supports(Sender, IWebExceptionHandler, Intf) then
try
Intf.HandleException(Exception(ExceptObject), Handled);
except
Handled := True;
System.SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
if (not Handled) then
System.SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
The line Intf.HandleException(Exception(ExceptObject), Handled) calls the TCustomWebDispatcher.HandleException procedure. This procedure checks for the existance of a OnException event handler in the TWebModule descendant in your application. If it exists (I would recommend it) the event handler procedure is called with the var argument 'Handled' set to true.
procedure TCustomWebDispatcher.HandleException(E: Exception; var Handled: Boolean);
begin
Handled := False;
if Assigned(FOnException) then
begin
Handled := True;
if Response <> nil then
Response.StatusCode := 500; { Allow the user to override the StatusCode }
FOnException(Self, E, Handled);
if Handled and (Response <> nil) then
Response.SendResponse;
end;
if not Handled then
if Response <> nil then
begin
Response.Content := Format(sInternalApplicationError, [E.Message, Request.PathInfo]);
if not Response.Sent then
begin
Response.StatusCode := 500;
Response.SendResponse;
end;
Handled := True;
end;
end;
When the handler returns there are two situations:
Handled is True; The response will be sent which leads to an new Exception. This one will be propagated back to the calling code (the procedure in the first block) so ShowException will be called.
Handled is False; Response <> nil will evaluate to True so when Response.Sent is False the same thing will happen.
I solved this by trying to send the response from the OnException event handler. When it fails an exception is raised. In that case I'm setting the Field 'FResponse' of the read only property 'Response' of my TWebModule's descendant to 'nil'. However, this field is a private field so I used some rtti to be able to access it.
procedure TwbmWebBrokerServerModule.UnlinkResponseObject;
begin
inherited;
TRTTIContext.Create.GetType(ClassInfo).GetField('FResponse').SetValue(Self, TValue.From(nil));
end;
procedure TwbmWebBrokerServerModule.WebModuleException(Sender: TObject; E: Exception; var Handled: Boolean);
begin
uVMI_Tools.AppendToLog(E.Message + ' (' + Request.PathInfo + ')');
if Request.Accept.Contains(CONTENTTYPE_APPLICATION_JSON) then
begin
Response.Content := StringToJSON(E.Message);
Response.ContentType := CONTENTTYPE_APPLICATION_JSON;
end
else
Response.Content := E.Message;
try
Response.SendResponse;
except
UnlinkResponseObject;
end;
end;
So when an exception occurs while sending the response, the property 'Response' is set to nil so there will be no attempt to send the response again.
There is no need to destroy the response object, assinging nil to it will suffice. It's because the Response object is created within TIdCustomHTTPServer class which just passed it to the TWebModule descendant. (Actually to the TWebModule's parent class TCustomWebDispatcher)