Error hooks non called in some error situations

Sorry, I was not correct … actually in the case I’ve shown above the UncaughtExceptionHandlers is called.

The situation where none of the above handlers is called is when I add a validator

container.Register<ServiceStack.FluentValidation.IValidator>(
new Hello2ValidatorCollection());

and the request does not match the validator requirement

@sabbadin12 Would you be able to provide a small project that reproduces the problem? This would make it a lot quicker to track down what might be causing this problem for you.

how you suggest to do it ? it doesn’t seem I can upload a zip here

Create a Github repository, share via Dropbox or another file hosting provider?

Actually same use case using soap format will not pass through

either.

In other word, if the soap message contains a property that should be an integer and you pass a string as xml element value a string, the UncaughtExceptionHandlers will not be called and a generic 500 exception will be retunerd in text/html with a generic description “Input string was not in a correct format.” Stack Trace says:

[SerializationException: DeserializeDataContract: Error converting type: DeserializeDataContract: Error converting type: There was an error deserializing the object of type Geographic.Http.Services.CastError. Content cannot be converted to the type Int. Line 1, position 151.]
   ServiceStack.Serialization.DataContractSerializer.DeserializeFromString(String xml, Type type) +167
   ServiceStack.Host.Handlers.SoapHandler.ExecuteMessage(Message message, RequestAttributes requestAttributes, IRequest httpReq, IResponse httpRes) +1037

The point is: why the UncaughtExceptionHandlers has not been called while same use case for json format will be?

There you go

the starting host solution is SS_ASPNET_01

on the swagger page you can call
http://localhost:56440/hello2/abc
and you break into UncaughtExceptionHandler …
but if you pass
http://localhost:56440/hello2/-23 (there is a validator that requires the number be < -1 … )
you wont’ break in any error handler , and not even in the globalresponsefilter , only the onendrequest is hit …

what we are trying to achieve is

  1. hooks to log any kind of errors in the service (but the 404)
  2. put a customized mesage in the ResponseStatus.Message in all error cases

thank you
enrico sabbadin

@sabbadin12 thx for the repro we should now be raising ServiceExceptions for ValidationErrors in this commit.

@cris We should also be raising Service and Uncaught Exceptions for SOAP requests.

These changes is available from v4.0.41+ that’s now available on MyGet.

thank you ,
two more things :
a) it seems that Onendrequest is not called for soap calls
b) it would be nice to have a appHost.EndRequest.Add(…); instead of having to override Onendrequest

thanks best regards
enrico

@sabbadin12 OnEndRequest should now be called for SOAP calls.

You can also now use AppHost.OnEndRequestCallbacks to register callbacks.

These changes are now available on MyGet.

Now UncaughtExceptionHandlers will be called for soap call as well, but only if the issue occurs on the input side (as per sample case pointed out here above).

In case the issue occurs on the output side, UncaughtExceptionHandlers will not be called and a generic html output will be returned including the stack trace

[SerializationException: Member RequiredProperty in type ResponseCustomizerSoapOutputDeserializationExceptionResponse cannot be serialized. This exception is usually caused by trying to use a null value where a null value is not allowed. The 'RequiredProperty' member is set to its default value (usually null or zero). The member's EmitDefault setting is 'false', indicating that the member should not be serialized. However, the member's IsRequired setting is 'true', indicating that it must be serialized. This conflict cannot be resolved. Consider setting 'RequiredProperty' to a non-default value. Alternatively, you can change the EmitDefaultValue property on the DataMemberAttribute attribute to true, or changing the IsRequired property to false.]

//ServiceModel
[DataContract(Namespace = Constants.SVC_NS)]
public class ResponseCustomizerSoapOutputDeserializationException
{
}

[DataContract(Namespace = Constants.SVC_NS)]
public class ResponseCustomizerSoapOutputDeserializationExceptionResponse
{
	[DataMember(EmitDefaultValue = false, IsRequired = true)]
	public string RequiredProperty { get; set; }
}

//ServiceInterface
public object Any(ResponseCustomizerSoapOutputDeserializationException request)
{
	return new ResponseCustomizerSoapOutputDeserializationExceptionResponse();
}

Response SOAP Serialization exceptions should now fire IAppHost.ServiceExceptionHandlers from this commit.

This change is available from v4.0.41+ that’s now available on MyGet.

Hi Demmis,
Thanks for your quik reply/fix, but IMHO now is even worst:
The hook will be now called after OnEndRequest, so I’m not able to customize the response and now, if I do not handle it, my serialization error on the output side disappear, if I do handle it,I’m not able to distinguish between normal service exception and serialization exception.

Why you decide to handle it as UncaughtExceptionHandlers instead of UncaughtExceptionHandlers?
IMHO should be handled as an UncaughtExceptionHandlers: serialization issue should be always treat in the same way no matter if the error occurs during the input o during the output.

Said so, please make the output serialization exception handled as UncaughtExceptionHandlers and fire it before OnEndRequest or rollback to previous implementation.

Since we know the Service that’s executed it fires the IAppHost.ServiceExceptionHandlers but it’s now been modified it so OnEndRequest() is called after the handlers. This change is available on MyGet.

If you prefer you can modify it to call UncaughtExceptionHandlers by overriding IAppHost.WriteSoapMessage, e.g:

public override void WriteSoapMessage(IRequest req, System.ServiceModel.Channels.Message message, Stream outputStream)
{
    try
    {
        using (var writer = XmlWriter.Create(outputStream, Config.XmlWriterSettings))
        {
            message.WriteMessage(writer);
        }
    }
    catch (Exception ex)
    {
        OnUncaughtException(req, req.Response, req.OperationName, ex);
    }
    finally
    {
        HostContext.CompleteRequest(req);
    }
}

Hi,
now ServiceExceptionHandlers has been called before OnEndRequest, but still I’m not able to distinguish between exception occured within the service method or outside such as serialization during the output.
An other issue with this new changes is the retuned object from the ServiceExceptionHandler will not be written in the output, but the previous one will be (the one created before the serialization error raised)

the override you proposed has 2 issue: a minor one

HostContext.CompleteRequest(req);

is internal. So I tried to call it via reflection

the major one is the output contains “2 outputs”: the original one before the serialization error occurs + the custom one I create through the custom uncaughtException handler. In short the response buffer has not been clean it out.My uncaughtExceptionHandler implementation down below (based on \src\ServiceStack\HttpResponseExtensionsInternal.cs)

private void uncaughtExceptionHandler(IRequest req, IResponse res, string operationName, Exception ex)
{
    //my output customization
	var errorDto = createResponseStatusForUncaughtException(operationName, ex);

	var httpReq = req;
	var httpRes = res;
	var contentType = httpReq.ResponseContentType;
	var statusCode = (int) HttpStatusCode.InternalServerError;


	if (httpRes.ContentType == null || httpRes.ContentType == MimeTypes.Html)
	{
		httpRes.ContentType = contentType;
	}
	if (HostContext.Config.AppendUtf8CharsetOnContentTypes.Contains(contentType))
	{
		httpRes.ContentType += ContentFormat.Utf8Suffix;
	}

	//TODO VERIFY IF StatusDescription EVER USED in the response to the client
	if (httpRes.StatusDescription == null || httpRes.StatusDescription == "OK")
	{
		string statusDescription = (((dynamic) errorDto).Message).ToString();
		// if string is longer than 512 .net raises an exception
		httpRes.StatusDescription = statusDescription.Truncate(509) + "...";
	}

	httpRes.StatusCode = statusCode;

	ResponseSerializerDelegate serializer = null;

	if (contentType == "application/soap+xml")
	{
		// custom serializer to avoid an exception due to missing SoapMessage within the request in case of not-serializable input
		serializer = SerializeSoap12ToStream;
	}
	else
	{
		serializer = HostContext.ContentTypes.GetResponseSerializer(contentType);

	}

	if (serializer != null)
	{
		serializer(httpReq, errorDto, httpRes);
	}

	httpRes.EndHttpHandlerRequest(skipHeaders: true);
}

Instead of HostContext.CompleteRequest() you can call OnEndRequest() directly.

The ResponseBuffer could be dirty at this point so I’ve avoided writing the response but have changed it in this commit to write it if the ResponseStream is reset-able, i.e. if it’s using a BufferedStream. The test also shows it uses a buffered output stream for SoapDeserializationException requests, e.g:

if (dto is SoapDeserializationException)
    req.Response.UseBufferedStream = true;

You can determine when the exception was thrown by setting a flag in IRequest.Items, e.g:

try {
    return response;
} finally {
    base.Request.Items["serviceCompleted"] = true;
}

I’ll have this deployed to MyGet after a few hours.

Change is now on MyGet.

Tomorrow at work I’ll try the new build.
Not sure I understood the strategy to make an assumption on a specific dto: serialization issue may occur on any dto.

The goal is to return a custom dto in case that serialization error occured, not necessary the one related to the request, same as per uncaught exception handler since the proper response dto is not serializable at that time/during that specific request.

Should I do it explictly on each service method, or are you going to add it? If I have to do it, which is the best way to do it as AOP(aspect)?

There’s no strategy or assumption, it’s making it implicit which test requires a buffered response so any non-default configuration is limited to just the tests that need it.

You can look at req.Response.Dto != null which will be populated with the Service’s response to determine if the Service executed successfully.

First of all thanks a lot for all your effort on this matter: we really appreciate it.
Your last checkin really helps a lot.

The decision / remedy cannot be based on a specific DTO: should work for any serialization exception. In order to accomplish this I made those changes

// my service serviceException handler

private object serviceExceptionHandler(IRequest httpReq, object request, Exception exception)
{
	IHasResponseStatus dto = null;
	var errorResponseDto = DtoUtils.CreateErrorResponse(request, exception);
	var httpError = errorResponseDto as IHttpError;
	if (httpError != null)
	{
		dto = httpError.Response as IHasResponseStatus;
		if (dto != null)
		{
			// no worries about next call: I use it to customize my response DTO.ResponseStatus property 
			FillResponseStatus(dto);
		}				
	}

	// Deserialization error after the service completed successfully
	if (httpReq.Response.Dto != null && dto != null)
	{
		return dto.ResponseStatus;
	}

	return errorResponseDto;
}

// ServiceStackHost.Runtime.cs -> WriteSoapMessage
// MyAppHostBase

public override void WriteSoapMessage(IRequest req, System.ServiceModel.Channels.Message message, Stream outputStream)
{
	try
	{
		using (var writer = XmlWriter.Create(outputStream, Config.XmlWriterSettings))
		{
			message.WriteMessage(writer);
		}
	}
	catch (Exception ex)
	{
		// my serviceException handler does not return, in this scenario, the response DTO related to the incoming request
		// but a generic DTO such as ResponseStatus
		var response = OnServiceException(req, req.Dto, ex);
		if (response == null || !outputStream.CanSeek)
			return;

		outputStream.Position = 0;
		try
		{
			//message = SoapHandler.CreateResponseMessage(response, message.Version, req.Dto.GetType(),
			message = SoapHandler.CreateResponseMessage(response, message.Version, response.GetType(),
				req.GetSoapMessage().Headers.Action == null);
			using (var writer = XmlWriter.Create(outputStream, Config.XmlWriterSettings))
			{
				message.WriteMessage(writer);
			}
		}
		catch (Exception exc)
		{
			Console.WriteLine(exc);
		}
	}
	finally
	{
		//HostContext.CompleteRequest(req);
		OnEndRequest(req);
		
		// Did you test your code with a IIS hosted scenario?
		// If I do not add following check/flush, self-hosted scenario(self hosted test) works with no problem 
		// but under IIS response is empty...
		if (req is AspNetRequest)
		{
			req.Response.Flush();
		}
	}
}

In order to be generic, I use buffered stream for any soap request
// MyAppHostBase

public override void Configure(Container container)
{
	...

	GlobalRequestFilters.Add((req, res, dto) =>
	{
		// https://github.com/ServiceStack/ServiceStack/commit/d412dc9840e8c667b921818be5ba4ca00791c336
		if (req.ContentType != null && req.ContentType.ToLower().Contains("soap"))
		{
			req.Response.UseBufferedStream = true;
		}
	});
}