Servicestack-RabbitMq: Return response type in message headers - servicestack

Is there any way to add the type of the response dto to the rabbitmq response message's headers collection?
(My consumer is using spring's rabbitmq handler which seems to depend on explicit type information inside the mq header when deserializing.)
Currently servicestack's mq producer already returns serveral headers, such as "content_type='application/json".
I am in need of an additional header, e.g. "typeId"="HelloResponse", so that the consuming web app knows how to deserialize the message, even in RPC cases where the response queue name is some kind of GUID.
Is there some kind of configuration which would enable me to archieve such an behaviour? Or some hook before the message gets published so that I can add the header myself?

I've added support for automatically populating the Message Body Type in RabbitMQ's IBasicProperties.Type as well as adding support for both Publish and GetMessage Filters in this commit.
Here's an example of configuring a RabbitMqServer with custom handlers where you can modify the message and its metadata properties when its published and received:
string receivedMsgApp = null;
string receivedMsgType = null;
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};
mqServer.RegisterHandler<Hello>(m =>
new HelloResponse { Result = "Hello, {0}!".Fmt(m.GetBody().Name) });
mqServer.Start();
Once Configured any message published or received will go through the above handlers, e.g:
using (var mqClient = mqServer.CreateMessageQueueClient())
{
mqClient.Publish(new Hello { Name = "Bugs Bunny" });
}
receivedMsgApp.Print(); // app:mq:Hello.In
receivedMsgType.Print(); // Hello
using (IConnection connection = mqServer.ConnectionFactory.CreateConnection())
using (IModel channel = connection.CreateModel())
{
var queueName = QueueNames<HelloResponse>.In;
channel.RegisterQueue(queueName);
var basicMsg = channel.BasicGet(queueName, noAck: true);
var props = basicMsg.BasicProperties;
props.Type.Print(); // HelloResponse
props.AppId.Print(); // app:mq:HelloResponse.Inq
var msg = basicMsg.ToMessage<HelloResponse>();
msg.GetBody().Result.Print(); // Hello, Bugs Bunny!
}
This change is available from ServiceStack v4.0.33+ that's now available on MyGet.

Related

Configuring Twilio SMS from Azure Functions v2

I have some code where I'm reading messages off of an Azure Event Hub that I want to either send an email or send an SMS.
The email is working through send grid, but I'm not sure how to configure the SMS part though.
I think I'd want to use Twilio and here's a sample of what my code's like. The "messageCollector" works for sending Email since there's some configuration for SendGrid in the local json. How do I configure Twilio?
[FunctionName("SendAlert")]
public static async Task Run(
[EventHubTrigger("v1-email-hub", Connection = "EventHubConnection")] EventData[] events,
[SendGrid] IAsyncCollector<SendGridMessage> messageCollector,
[TwilioSms] IAsyncCollector<CreateMessageOptions> smsCollector,
[Inject] NotificationEventLogic eventLogic,
ILogger log)
{
foreach (EventData eventData in events)
{
string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);
var notificationEvents = JsonConvert.DeserializeObject<List<NotificationEvent>>(messageBody);
foreach (var ev in notificationEvents)
{
if (ev.NotificationEventType == NotificationEventType.Email)
{
var message = new SendGridMessage();
// ... ... make message and add it
await messageCollector.AddAsync(message);
}
else if (ev.NotificationEventType == NotificationEventType.SMS)
{
// Not sure how to get this to work
var mobileMessage = new CreateMessageOptions(new PhoneNumber(ev.Data))
{
Body = $"Notification {ev.NotificationId}"
};
await smsCollector.AddAsync(mobileMessage);
}
// await smsCollector.AddAsync()
await eventLogic.CreateEventAsync(ev);
}
}
}
You will need to configure it in attribute
[TwilioSms(AccountSidSetting = "TwilioAccountSid", AuthTokenSetting = "TwilioAuthToken", From = "+1425XXXXXXX")]
as it mentioned in documentation
TwilioAccountSid This value must be set
to the name of an app setting that holds your Twilio Account Sid e.g.
TwilioAccountSid. If not set, the default app setting name is
"AzureWebJobsTwilioAccountSid".
TwilioAuthToken This value must be set to
the name of an app setting that holds your Twilio authentication token
e.g. TwilioAccountAuthToken. If not set, the default app setting name
is "AzureWebJobsTwilioAuthToken".

Creating a C# Amazon SQS Client in ServiceStack

There is some documentation on using Amazon SQS as an MQ Server forServiceStack Messaging API
But the message publisher is frontend web code and when you dig into the Email Contacts demo app, it is using Rabbit MQ.
There is a ServiceStack Email Contacts AWS App demo but it doesn't use the Messaging API.
Trying to use the Rabbit MQ Integration Test in Email Contacts as an example:
[Test]
public void Can_Send_Email_via_MqClient()
{
var mqFactory = new RabbitMqMessageFactory();
using (var mqClient = mqFactory.CreateMessageQueueClient())
{
mqClient.Publish(new EmailContact { ContactId = 1, Subject = "UnitTest MQ Email #1", Body = "Body 1" });
mqClient.Publish(new EmailContact { ContactId = 1, Subject = "UnitTest MQ Email #2", Body = "Body 2" });
}
}
I quickly confused and lead astray and the ServiceStack API for Sqs seems very different than RabbitMQ. I cannot even seem to be able to use a strongly type POCO as a Message:
[Fact(DisplayName = "Tests that a successful message is published and received")]
public async void TestMessage()
{
var mqFactory = new SqsConnectionFactory("awskey", "awssecret", RegionEndpoint.USWest1);
using (IAmazonSQS mqClient = mqFactory.GetClient())
{
var req = new SendMessageRequest("query", "hello");
await mqClient.SendMessageAsync(req);
//mqClient.Publish(new Hello { Name = "World" });
//var rec = new ReceiveMessageRequest();
//await mqClient.Re
//var responseMsg = mqClient.Get<HelloResponse>(QueueNames<HelloResponse>.In);
//mqClient.Ack(responseMsg);
//responseMsg.GetBody().Result //= Hello, World!
}
}
Is there an example app using the ServiceStack Messaging API with SQS as the MQ Server?
There is a ServiceStack Email Contacts AWS App demo but it doesn't use the Messaging API.
Note AWS Apps does register the AWS SqsMqServer:
//EmailContacts
ConfigureSqsMqServer(container);
//..
private void ConfigureSqsMqServer(Container container)
{
container.Register<IMessageService>(c => new SqsMqServer(
AwsConfig.AwsAccessKey, AwsConfig.AwsSecretKey, RegionEndpoint.USEast1) {
DisableBuffering = true,
});
var mqServer = container.Resolve<IMessageService>();
mqServer.RegisterHandler<EmailContacts.EmailContact>(ExecuteMessage);
mqServer.Start();
}
There's also a number of examples in SqsMqServerTests.cs.
If you want to use ServiceStack MQ's high-level APIs, you'd need to use ServiceStack's MQ classes instead of AWS's SQS classes directly.
Basically it works like every other MQ Server, you can fetch an MQ Client from the IMessageFactory or IMessageService (registered in your AppHost) and use it to publish DTOs:
var mqFactory = HostContext.TryResolve<IMessageFactory>(); //or
//var mqFactory = HostContext.TryResolve<IMessageService>().MessageFactory;
using (var mqClient = mqFactory.CreateMessageQueueClient())
{
mqClient.Publish(new Hello { Name = "World" });
}
Although the preferred API within a ServiceStack Service is to use PublishMessage():
PublishMessage(new Hello { Name = "World" });
Note requests to the /oneway pre-defined endpoint are automatically published to the registered MQ Server.
In client Apps without a registered SqsMqServer you'd create a SqsMqMessageFactory:
var mqFactory = new SqsMqMessageFactory(new SqsQueueManager(...));
using (var mqClient = mqFactory.CreateMessageQueueClient())
{
mqClient.Publish(new Hello { Name = "World" });
}

Azure Service Bus GetBody<Stream> encoding cannot be determined

I'm trying to get a BrokeredMessage from AzureServiceBus in a .NET client and choose how to deal with the message based on the type of message coming in, but ContentType and other message properties are not set.
My test message sending looks like this:
var client =
QueueClient.CreateFromConnectionString(connectionString, queueName);
var message = new BrokeredMessage("test");
client.Send(message);
My code to receive is using GetBody so that I can inspect the serialized data and decide how to deal with it:
var stream = message.GetBody<Stream>();
string s = null;
using (StreamReader sr = new StreamReader(stream))
{
s = sr.ReadToEnd();
}
The problem is that "s" above ends up with what looks like it should be XML created from a DataContractSerializer, but it is strangely encoded. I've tried many encodings on the receiving side and none seem to get me valid xml. Example results:
#string3http://schemas.microsoft.com/2003/10/Serialization/�test
I see the serialization namespace and what looks like it should start with <string, but as you can see I'm getting control characters. Does any one know how I can try to get the serialized data here as valid XML so I can dynamically handle it?
TIA for any help.
To be really clear I want to test the body so I can do something like:
if (BodyIsString(s)) { do something }
if (BodyIsPerson(s)) { do something else }
If I could getbody twice this would be really easy.
As Sean Feldman mentioned when send message is string type we could use
var body = message.GetBody<string>();
to get message body, after I decompile the WindowsAzure.ServiceBus.dll then get the code:
public T GetBody<T>()
{
if (typeof (T) == typeof (Stream))
{
this.SetGetBodyCalled();
return (T) this.BodyStream;
}
if (!this.bodyObjectDecoded || this.bodyObject == null)
return this.GetBody<T>((XmlObjectSerializer) new DataContractBinarySerializer(typeof (T)));
this.SetGetBodyCalled();
return (T) this.bodyObject;
}
I find that if the send message
is not Stream type it will be DataContractBinarySerializer. so we also could get the message body with following way
var stream = message.GetBody<Stream>();
var messageBody = new DataContractSerializer(typeof(string)).ReadObject(XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max));
From the decompiled code we could know that if we send the stream message, we could get the message body with the way you mentioned.
Send stream message code :
var client = QueueClient.CreateFromConnectionString(connectionString, queueName);
var byteArray = Encoding.UTF8.GetBytes("test stream");
MemoryStream stream = new MemoryStream(byteArray);
client.Send(new BrokeredMessage(stream));
then receive message as you mentioned it should work:
var stream = message.GetBody<Stream>();
string s = null;
using (StreamReader sr = new StreamReader(stream))
{
s = sr.ReadToEnd();
}
Edit :According to update question:
If I could getbody twice this would be really easy.
we could clone the BrokerMessage
var newMessage = receiveMessage.Clone();
Edit2:
We also can get the message Properties to know the body type if we set it during sending. Take Label for example:
var message = new BrokeredMessage(object);
message.Label = "Type of message body";
client.Send(message);
While we receive the message we could get the message Label value then select the corresponding way to get the body.
You passed your payload as a string
var message = new BrokeredMessage("test");
therefore it was serialized as a string. Upon receive you should get the body as a string as well in the following manner:
var body = message.GetBody<string>();
You would use Stream if you'd actually construct your brokered message using a stream.

How to log the response message in a Registered Handler - ServiceStack RabbitMQ

Given this snippet of code:
//DirectApi
mqServer.RegisterHandler<LeadInformationInfo>(m =>
{
repository.SaveMessage(m as Message);
LeadInformationInfoResponse response = new LeadInformationInfoResponse();
try
{
var client = new JsonServiceClient(settingsFactory.GetMasterSetting("ProcessorApi:baseUri"));
response = client.Post(m.GetBody());
}
catch (WebServiceException webServiceException)
{
_log.Error("RegisterHandler<LeadInformationInfo>", webServiceException);
response = ((LeadInformationInfoResponse) webServiceException.ResponseDto);
response.CorrelationId = m.Id;
}
// Log response message here
return response;
}, 1);
I've gone to great lengths to make sure that a correlationId based off the original message Id property is propagated through the life of this message and any child messages spawned from this action. How do I get a handle on the response message so that I may log it in the handler? I only have access to the ResponseDto and not the message.
One of the reasons for this request is that the message queue client does not have access to the database, only the process that has the handler registered does. Hope that explains the situation better.
Just to clarify, this question is about persisting a MQ Response Message in the handler, the correlation Id is something that all messages in 1 request/response workflow will share. I'm also using ServiceStack ORMlite to persist the Message object, so querying this table by ID for troubleshooting is paramount.
Thank you,
Stephen
You're calling a WebService from within your MQ Handler:
var client = new JsonServiceClient(...);
response = client.Post(m.GetBody());
So there is no MQ Response which is only available in MQ Services. Although the WebService will return the response for the request that's sent so you can either use the CorrelationId on the MQ Request, otherwise you can have your Response DTO implement an interface like IHasCorrelationId and get it that way, e.g:
var correlationResponse = response as IHasCorrelationId;
if (correlationResponse != null)
{
var correlationId = correlationResponse.CorrelationId;
}
Create your own Instance of Message
As the Message<T> class is just a POCO if you wanted to create your own you can intialize your own instance:
var mqResponse = new Message<Response>(response);
If you only had the runtime late-bound type info, you can create one with:
var mqResponse = MessageFactory.Create(response);
Use RabbitMQ Message Filters
If you just wanted to log incoming and outgoing messages you can use the RabbitMQ Message Filters, e.g:
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};

How to send/receive messages through a web socket on windows phone 8 using the class ClientWebSocket?

The web socket is written in javascript by my colleague. I managed to connect. First of all I have to log in on the application using a test account. I have to send the email and password through a json. I have installed the Json.Net packet using NuGet.
Some code that I found on my reaserch is this, but I do not understand how to send my data using that segment.
var buffer = new byte[1024];
var segment = new ArraySegment<byte>(buffer);
webSocket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
Of course, I can use an object
User user=new User();
user.Email="bla#bla.com";
user.Password="pass";
string json = JsonConvert.SerializeObject(user);
But it will not be of any use because the method SendAsync accepts only byte type on segment.
All I want is to send that data, and if log in succeeds, I should receive other data (in Json format) about the user.
As a side note, I am quite new to web sockets, I used http protocols from ASP.NET WEB API 2.
I have no idea about Windows Phone 8, but by the code you pasted it seems similar to the regular .NET ClientWebSocket, so here you have some examples:
public static Task SendString(ClientWebSocket ws, String data, CancellationToken cancellation)
{
var encoded = Encoding.UTF8.GetBytes(data);
var buffer = new ArraySegment<Byte>(encoded, 0, encoded.Length);
return ws.SendAsync(buffer, WebSocketMessageType.Text, true, cancellation);
}
public static async Task<String> ReadString(ClientWebSocket ws)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[8192]);
WebSocketReceiveResult result = null;
using (var ms = new MemoryStream())
{
do
{
result = await ws.ReceiveAsync(buffer, CancellationToken.None);
ms.Write(buffer.Array, buffer.Offset, result.Count);
}
while (!result.EndOfMessage);
ms.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(ms, Encoding.UTF8))
return reader.ReadToEnd();
}
}
If something does not compile or exists in WP8, just find an equivalent.
#vtortola is a working example in case your data comes in multiple segmented messages, but if all data comes in a single message you don't need all those streams to read the message, you just need to do this:
public static async Task<String> ReadString(ClientWebSocket socket)
{
var reciveBuffer = new byte[32000];
var result = await socket.ReceiveAsync(new ArraySegment<byte>(reciveBuffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
return Encoding.ASCII.GetString(reciveBuffer , 0, result.Count);
}
If your message is splited in multiple segments or you don't know how your message is comming then you have to do like #vtortola
Also if you want to keep receiving messages you can do a while and call ReadString inside, like this:
while (socket.State == WebSocketState.Open)
{
var msg = ReadString(socket)
//do something with your message...
}

Resources