2

Our application uses temporary queues to direct service bus responses to the originating caller. We use the built-in ServiceStack.RabbitMq.RabbitMqServer to publish and handle messages.

Message<IReturn<ResponseDto>> message = BuildMessage(requestDto);

// get the temporary queue for the current IMessageQueueClient
string queueName = messageclient.GetTempQueueName();
message.ReplyTo = queueName;

// publish the message    
messageclient.Publish(message);

However, capturing the response directly (below) will fail if the call throws an exception.

IMessage<ResponseDto> responseMessage = messageclient.Get<ResponseDto>(queueName, timeOut);
messageclient.Ack(responseMessage);

ResponseDto response = responseMessage.GetBody();

The body of the response message will be a ServiceStack.ErrorResponse causing responseMessage.GetBody() to return an empty object. And the error is not returned as responseMessage.Error.

We get around this by getting the body of the message as the raw JSV string and validating the result.

IMessage<string> responseMessage = messageclient.Get<string>(queueName, timeOut);
messageclient.Ack(responseMessage);

// get the body of the message as a string
string messageBody = responseMessage.GetBody();

// parse as error response
var error = messageBody.FromJsv<ErrorResponse>();

// if no response status, assume good
if (error?.ResponseStatus != null)
    throw new Exception(error.ResponseStatus.Message);

// parse as response and return
return messageBody.FromJsv<ResponseDto>();

This works for most cases, however, if a string value in the returning RabbitMq message JSON contains a comma, the string is not contained in quotes in the message body JSV (this seems to be an issue in ServiceStack.Text when converting from JSON -> JSV using the JsonTypeSerializer) which results in that field being incomplete, and the subsequent field being ignored.

Is there a better way to retrieve the exception? Or is there a workaround for the serialization issue?

David
  • 1,143
  • 7
  • 9

1 Answers1

2

There should be no converting JSON into JSV, they're different formats. JSV uses CSV-style encoding and escaping which is illegal in JSON which requires all string values to be quoted and strings escaped with \.

Essentially you should be deserializing the message using the exact Serializer and Type used to serialize it, if you do this there shouldn't be any issues.

ServiceStack's Rabbit MQ support serializes complex Type payloads as JSON so it's not clear where the JSV is coming from.

If you need to access the raw text payload you should use Rabbit MQ's low-level APIs instead of ServiceStack's RabbitMQ high-level APIs which automatically tries to deserialize complex types messages behind-the-scenes.

To get the raw text body you can do something like:

var rabbitMqClient = messageclient as RabbitMqProducer;
var mqResult = rabbitMqClient.GetMessage(queueName, noAck: false);
var props = msgResult.BasicProperties;
var bodyString = msgResult.Body.FromUtf8Bytes(); //normally JSON
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Ok. So our use-case is a bit outside the scope of the built-in implementation and will require a bit of customization? The JSON -> JSV occurs when the Rabbit MQ `.Get` is called - internally it calls `msg.Body.FromUtf8Bytes().FromJson()` which converts to a JSV string. – David Jun 27 '17 at 14:26
  • @David I don't think the use-case is the issue, if you're getting serialization errors I'm assuming you're not using the same serializer + Type that's used to serialize the body to deserialize it. – mythz Jun 27 '17 at 14:30
  • @David [JSON](http://www.json.org/) is not [JSV](http://docs.servicestack.net/jsv-format). The `.FromJson` extension method should only be used to deserialize JSON. – mythz Jun 27 '17 at 14:31
  • The publisher expects a `ResponseDto` as a response - but if there is an exception when the message is handled, the body of the response will be a `ErrorResponse`. I attempted to handle this case by getting the message body as a string using `IMessageQueueClient.Get`. Internally that calls `.FromJson` which will convert a JSON string to a JSV string. But will return malformed JSV if a value contains a comma (e.g. `"{\"HelloWorld\":\"Hello, World\"}".FromJson()` returns `"{HelloWorld:Hello, World}"`) – David Jun 27 '17 at 14:49
  • It's meaningless trying to deserialize a JSON object into a string, you already have JSON string, the behavior of coercing a JSON object into a scalar string is undefined. Ideally you should already know what Type it is you want to deserialize and use that, if you don't know the Type you can use the [Dynamic JSON APIS](https://github.com/ServiceStack/ServiceStack.Text#supports-dynamic-json) to parse and inspect JSON objects in loose-typed collections. – mythz Jun 27 '17 at 15:24
  • 1
    We know the expected type. The Rabbit MQ handler doesn't attach the error to the message if a ReplyTo is specified, it just returns the error as the body of the response. I followed your advice and extended the `RabbitMqQueueClient` to properly attach the error to the message in this case. Thansk! – David Jun 27 '17 at 17:20