NServicebus receive messages without all the NServicebus specific stuff - c#

I am new to NServicebus and have struggled to find an answer in the documentation.
I am trying to receive a message that is posted to Amazon SQS in a simple JSON format like this:
"MyMessage": {
"Id": 1,
"Name": "Name",
"Field1": "text",
"Field2": 1,
"Field3": false
}
However whenever this gets sent to the queue my NServicebus subscriber says it is a poison message and doesn't try to handle it.
I realize that this message is missing a lot of NServicebus stuff because when I publish a message via NServicebus it looks like this:
{
"Headers": {
"NServiceBus.MessageId": "a244a014-e331-41e6-b6ca-aed6011af905",
"NServiceBus.MessageIntent": "Publish",
"NServiceBus.ConversationId": "e42f0308-4c51-4787-ade0-aed6011af90f",
"NServiceBus.CorrelationId": "a244a014-e331-41e6-b6ca-aed6011af905",
"NServiceBus.OriginatingMachine": "DESKTOP-1234567",
"NServiceBus.OriginatingEndpoint": "endpoint",
"$.diagnostics.originating.hostid": "da7dce712dfbc0f093aa30eb7f25d2b4",
"NServiceBus.ContentType": "application/json",
"NServiceBus.EnclosedMessageTypes": "Type",
"NServiceBus.Version": "7.7.3",
"NServiceBus.TimeSent": "2022-07-18 17:10:16:400164 Z"
},
"Body": "Base 64 encoded string here",
"S3BodyKey": null
}
The problem is the message I am receiving is not published via NServicebus and comes in the format I showed above. It doesn't have all of the headers and a body that is base64 encoded.
Is there a way to set up NServicebus to be able to receive and handle such a message? Or is it just not built to handle stuff like this?
Note: This is a .Net 6 application
Edit: I found this article that mentions how NServicebus can receive messages without all the headers, but it doesn't mention how.
https://www.bradjolicoeur.com/Article/nsb-features-message-headers

What you want is called Native Send and is actually documented. You have to conform your messages to the format NServiceBus expects in order to be able to have handlers correctly process it.
A native send function would look like this:
public static async Task SendMessage(IAmazonSQS sqsClient, string queue, string messageBody, Dictionary<string, string> headers)
{
var bodyBytes = Encoding.UTF8.GetBytes(messageBody);
var base64Body = Convert.ToBase64String(bodyBytes);
var serializedMessage = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
Headers = headers,
Body = base64Body,
});
var queueUrlResponse = await sqsClient.GetQueueUrlAsync(QueueNameHelper.GetSqsQueueName(queue));
await sqsClient.SendMessageAsync(queueUrlResponse.QueueUrl, serializedMessage);
}
To use this you'd need to specify message type and some other header values:
await SendMessage(
sqsClient: client,
queue: "samples-sqs-nativeintegration",
messageBody: "{Property:'PropertyValue'}",
headers: new Dictionary<string, string>
{
{"NServiceBus.EnclosedMessageTypes", "MessageTypeToSend"},
{"NServiceBus.MessageId", "99C7320B-A645-4C74-95E8-857EAB98F4F9"}
}
);

Related

How to encrypt the body of a request with GraphQL and Hot Chocolate

I have to implement an API using GraphQL with C# and Hot Chocolate.
One of the requirements is that the body of the request must be encrypted.
For instance, if I call my "Hello World" service with this body:
query{
hello(name: "stackoverflow.com")
}
And the response of the service is this:
{
"data": {
"hello": "Hello, stackoverflow.com "
}
}
I have to be able to send a body fully encrypted with some algorithm, lets say SHA256, like this:
//This is the query of graphql encrypted in SHA256
5777bb8378c6caf7f29e7b6aae9ddcb168cbba74ffd60dbe6ef21c2f70b16736
Then, decrypt the request and get the data to send as reponse.
My first aproach was to add an Interceptor
Get the HttpRequest body, decrypt, and then continue with the normal flow.
public class HttpRequestInterceptor : DefaultHttpRequestInterceptor
{
public override ValueTask OnCreateAsync(HttpContext context,
IRequestExecutor requestExecutor,
IQueryRequestBuilder requestBuilder,
CancellationToken cancellationToken)
{
//Get the raw body of the request
var rawBody = GetRequestBodyAsync(context.Request);
//Decrypt the raw body and set the decrypted body into the HttpContext request body
GetDecryptedBodyAsync(context, rawBody.Result);
return base.OnCreateAsync(context, requestExecutor, requestBuilder, cancellationToken);
}
private async void GetDecryptedBodyAsync(HttpContext context, string rawBody)
{
var requestContent = new StringContent(rawBody, Encoding.UTF8, "application/json");
context.Request.Body = await requestContent.ReadAsStreamAsync();
}
}
The problem is when I test this aproach, it doesn't work because the encrypted hash input as body is not valid:
{
"errors": [
{
"message": "Unexpected token: Name.",
"locations": [
{
"line": 1,
"column": 1
}
],
"extensions": {
"code": "HC0011"
}
}
]
}
Is there a way to do this?
So there is a difference between encryption and hashing.
SHA256 is hashing, and has nothing to do with encryption. If you has your document, there is no way to retrieve the original document.
If you do not want to send Queries over the wire then maybe you are looking for Persisted Queries:
https://chillicream.com/docs/hotchocolate/v12/performance/persisted-queries
or as a video: https://www.youtube.com/watch?v=ZZ5PF3_P_r4
If this doesnt help you, and you want to do encryption, then you have to first define what you want to protect your data from.
Maybe TLS is already enough of a encryption, then you just have to force the user to use HTTPS.
If you end up using some kind of custom end to end encryption, then you will likely have to do this before the HotChocolate execution. So as a AspNet Core Middleware, rahter than a GraphQL component.

Unable to get Topic Exchange to Work in RabbitMQ AMQP 1.0

I am finding it very difficult to work with RabbitMQ and AMQP 1.0, particularly as it pertains to topic exchanges using AMQPNetLite. I am unable to send a message using a topic exchange to a specific queue. I'm not even using wildcards.
My situation is super simple too. I have one topic exchange. I have one queue that the topic exchange sends to. When I send to the topic exchange, the queue never receives the message.
test.exchange:
bind: testqueue - routing key: test
testqueue:
bound to exchange with routing key: test
The AMQP 1.0 documentation says that the "Subject" is the routing key right? Well when I use AMQPNetLite to send to RabbitMQ, it appears to connect, and the topic appears to have received the message, but it is never routed to the queue.
Here's the entire code:
var rabbitMqAddress = $"amqp://127.0.0.1:5672";
var address = new Address(rabbitMqAddress);
var producerName = $"Producer-test.topic-{Time.GetTimeStamp()}";
var connection = new Connection(address, null, new Open
{
ContainerId = Guid.NewGuid().ToString(),
ChannelMax = 64,
}, null);
var session = new Session(connection);
var senderLink = new SenderLink(session, producerName, "/topic/test.exchange");
senderLink.Send(new Message
{
BodySection = new AmqpValue { Value = "test 123" },
Properties = new Properties
{
Subject = "test",
}
});
The image proves the binding. Is there something I'm missing?
i think you mix two ways of doing it.
Either you publish to address "/topic/test" -where test is your routingkey
OR
you publish to "/exchange/test.exchange" and set the Subject-property to "test".
Both works. if you use the "/topic/"-prefix in your address you are going through the default "amq.topic"-exchange and not your own "test-exchange".
made sense?
more information in the "Routing and Addressing"-secion here: https://github.com/rabbitmq/rabbitmq-amqp1.0

MassTransit: When using UseDelayedExchangeMessageScheduler messages sent with ScheduleSend ends up in the skipped queue

I'm trying to send a Scheduled Message using the UseDelayedExchangeMessageScheduler along with the rabbitmq_delayed_message_exchange plugin. I do setup the bus like this:
public void StartUpBus()
{
_bus = Bus.Factory.CreateUsingRabbitMq(ConfigureBus);
_bus.Start();
}
private void ConfigureBus(IRabbitMqBusFactoryConfigurator busConfigurator)
{
var host = busConfigurator.Host(new Uri(_connectionInfo.ConnectionString), h =>
{
h.Username(_connectionInfo.User);
h.Password(_connectionInfo.Password);
});
busConfigurator.UseDelayedExchangeMessageScheduler();
busConfigurator.ReceiveEndpoint(host, "schedule-send-endpoint.inbox", endpoint => {
endpoint.PrefetchCount = 1;
endpoint.Consumer( () => new AScheduledConsumer() );
});
}
The consumer is simple as this
public class AScheduledConsumer : IConsumer<AScheduledMessage>
{
public Task Consume(ConsumeContext<AScheduledMessage> context)
{
return Console.Out.WriteLineAsync($"Message received at {DateTime.Now}");
}
}
Here is the message I'd like to send
// Simple role interface to easily identify bus travelling data
public interface IMessage
{
}
public class AScheduledMessage : IMessage
{
}
Then I try to send the message with something like this
var message = new AScheduledMessage();
var delay = TimeSpan.FromMinutes(1);
Uri destinationUri = new Uri("rabbitmq://localhost/schedule-send-endpoint.inbox");
await Console.Out.WriteLineAsync($"Message sent at {DateTime.Now}");
return _bus.ScheduleSend(
destinationUri,
delay,
message
);
I expected to have the message delivered about one minute after the send, but the message ends in the _skipped queue. Here after the message as it is dumped by the "Get Message" function in the RabbitMq management ui.
The server reported 0 messages remaining.
Exchange schedule-send-endpoint.inbox_skipped
Routing Key
Redelivered ○
Properties
message_id: d7040000-4392-98e7-c8e1-08d7e3d61e54
correlation_id: d7040000-4392-98e7-cc70-08d7e3d61e49
delivery_mode: 2
headers:
Content-Type: application/vnd.masstransit+json
MT-Host-Assembly: Infrastructure.Messaging.RabbitMq.Test.ConsoleApp
MT-Host-AssemblyVersion: 1.0.0.0
MT-Host-FrameworkVersion: 4.0.30319.42000
MT-Host-MachineName: GABROS-NB
MT-Host-MassTransitVersion: 5.2.1.1808
MT-Host-OperatingSystemVersion: Microsoft Windows NT 6.2.9200.0
MT-Host-ProcessId: 26984
MT-Host-ProcessName: Infrastructure.Messaging.RabbitMq.Test.ConsoleApp.vshost
MT-Reason: dead-letter
infrastructure.correlation-id: 029ea5c6-e5ee-44b7-8851-84d3b6ebd191
infrastructure.user-id: anonymous
publishId: 1
content_type: application/vnd.masstransit+json
Payload
1649 bytes
Encoding: string
{
"messageId": "d7040000-4392-98e7-c8e1-08d7e3d61e54",
"correlationId": "d7040000-4392-98e7-cc70-08d7e3d61e49",
"conversationId": "d7040000-4392-98e7-29ed-08d7e3d61e5d",
"sourceAddress": "rabbitmq://localhost/bus-GABROS-NB-Infrastructure.Messaging.RabbitMq.Test.ConsoleApp.vshost-4hnyyynd1kcqqhmibdm68io7fu?durable=false&autodelete=true",
"destinationAddress": "rabbitmq://localhost/schedule-send-endpoint.inbox",
"messageType": [
"urn:message:MassTransit.Scheduling:ScheduleMessage[[Infrastructure.Messaging.Test:AScheduledMessage]]",
"urn:message:MassTransit.Scheduling:ScheduleMessage"
],
"message": {
"correlationId": "d7040000-4392-98e7-cc70-08d7e3d61e49",
"scheduledTime": "2020-04-18T20:21:47.1796308Z",
"payloadType": [
"urn:message:Infrastructure.Messaging.Test:AScheduledMessage",
"urn:message:Infrastructure.Messaging:IMessage"
],
"destination": "rabbitmq://localhost/schedule-send-endpoint.inbox",
"payload": {}
},
"sentTime": "2020-04-18T20:21:46.8178828Z",
"headers": { },
"host": {
"machineName": "GABROS-NB",
"processName": "Infrastructure.Messaging.RabbitMq.Test.ConsoleApp.vshost",
"processId": 26984,
"assembly": "Infrastructure.Messaging.RabbitMq.Test.ConsoleApp",
"assemblyVersion": "1.0.0.0",
"frameworkVersion": "4.0.30319.42000",
"massTransitVersion": "5.2.1.1808",
"operatingSystemVersion": "Microsoft Windows NT 6.2.9200.0"
}
}
And these are the Exchanges I find in the RabbitMQ
Name Type Features Message rate in Message rate out+/-
(AMQP default) direct D
Infrastructure.Messaging.Test:AScheduledMessage fanout D
amq.direct direct D
amq.fanout fanout D
amq.headers headers D
amq.match headers D
amq.rabbitmq.trace topic D I
amq.topic topic D
bus-GABROS-NB-Infrastructure.Messaging.RabbitMq.Test.ConsoleApp.vshost-4hnyyynd1kcqqysnbdm6jy77ny fanout AD
schedule-send-endpoint.inbox fanout D 0.00/s 0.00/s
schedule-send-endpoint.inbox_skipped fanout D 0.00/s 0.00/s
What I find strange is the total absence of an x-delay header in the message and no x-delayed-message Exchange created in rabbitMq, as if the UseDelayedExchangeMessageScheduler was totally ignored ... I think I'm doing something wrong, but really can't find the culprit !
--- UPDATE ---
As pointed out by #ChrisPatterson, the message must be sent from a MessageScheduler.
While still didn't find a solution for MassTransit 5.2.1, this code works using MassTransit v6.4.2 and dotnet core 2:
var message = new AScheduledMessage();
var delay = TimeSpan.FromMinutes(1);
Uri destinationUri = new Uri("rabbitmq://localhost/schedule-send-endpoint.inbox");
var ms = new MessageScheduler(new DelayedExchangeScheduleMessageProvider(bus, bus.Topology as IRabbitMqHostTopology))
await Console.Out.WriteLineAsync($"Message sent at {DateTime.Now}");
return ms.ScheduleSend(
destinationUri,
delay,
message
);
If you are scheduling messages from the bus, and not inside a consumer, you need to use a message scheduler class. What you see happening in your example above is that it is sending to the destinationAddress as if that was Quartz, but it isn't. To use the delayed exchange outside of a ConsumeContext you have to create a MessageScheduler class, and pass it the RabbitMQ Delayed Exchange Scheduler Provider.
It isn't obvious, I will update the documentation and try to make it easier for scheduling from the bus using non-Quartz schedulers.
I've also added a new method, CreateRabbitMqMessageScheduler that is an extension method on IBus that basically creates the required components to schedule using the delayed exchange:
if (bus.Topology is IRabbitMqHostTopology topology)
return new MessageScheduler(new DelayedExchangeScheduleMessageProvider(bus, topology));
That is what's done under the hood.

Web push C# library function

I am currently working on web push notifications and am at the last stage of using the web push libraries to send the notification.
I am using the C# web push library here. However, I do not see a notification when on the page or when not on it.
I am attaching my code below:
I wrote the code in my store subscription method so it could be one of the issues.
[HttpPost]
public void StoreSubscription(string [] publicKey, string [] auth, string notificationEndPoint )
{
var pushEndpoint = notificationEndPoint;
var pushAuth = auth[0].ToString();
var pushP256DH = publicKey[0].ToString();
var subject = "mailTo:hhhhhhh#gmail.com";
var uPublicKey = "yyzzxx";
var privateKey = "xxyyzz";
System.IO.File.WriteAllText(#"\Desktop\Subscription.txt", pushEndpoint);
var subscription = new PushSubscription(pushEndpoint, pushP256DH, pushAuth);
var gcmAPIKey = "AAAA";
var vapidDetails = new VapidDetails(subject, uPublicKey, privateKey);
var webPushClient = new WebPushClient();
try
{
webPushClient.SetGCMAPIKey(gcmAPIKey);
webPushClient.SendNotification(subscription, "payload", gcmAPIKey);
}
catch (WebPushException exception)
{
Console.WriteLine("HTTP status Code:" + exception.StatusCode);
}
}
And my service worker code is as follows for handling the push:
self.addEventListener('push', function (event) {
debugger
var body;
if (event.data) {
body = event.data.text();
} else {
body = 'Push message no payload';
}
var options = {
body: body,/*'This message was generated from a push'*/
icon: '/Images/noun_Pushing_1823359.png',
vibrate: [100, 200, 100, 200, 400],
data: {
dateOfArrival: Date.now(),
primaryKey: '2'
},
actions: [
{
action: 'explore', title: 'Explore this new world',
icon: '/Images/noun_Check Mark_4870.png'
},
{
action: 'close', title: 'Close',
icon: '/Images/noun_Close_887590.png'
},
]
};
event.waitUntil(
self.registration.showNotification('Push Notification', options)
);
});
I have been stuck on this for almost a long time now and very new to promise, service worker and push and notification apis.
the function is getting hit and does not throw any exception. Also, when i put a debugger in the service worker, it does not get hit so apparently the push is not getting handles.I might be completely wrong on this.
I had the same issue. I base 64 encoded my auth and p256dh used to create the subscription.
If there is no exception it could mean the JSON payload is valid JSON but in the wrong format for the service worker. I Changed my payload to a valid JSON object for my service worker (Angular):
{ "notification": {"title": "message title", "body": "message body"} }
Please see docs... https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification
Sent notification and hit the service worker.
I was using Vapid Details not GCMApiKey.

Error while deserializing Azure ServiceBus Queue message sent from node.js (azure sdk)

Here's my scenario:
I'm sending an Azure ServiceBus Queue message from Node.js using the node azure sdk like so:
var message = {
body: JSON.stringify({ foo: 'Bar' })
};
serviceBusService.sendQueueMessage('myQueue', message, function (error) {
if (!error) {
console.log('msessage sent');
}
});
I have a c# worker role that is listening to the Queue:
QueueClient Client = QueueClient.CreateFromConnectionString(connStr, QueueName);
Client.OnMessage((receivedMessage) =>
{
var body = receivedMessage.GetBody<string>();
});
When the GetBody method gets executed, i get the following error:
There was an error deserializing the object of type System.String. The input source is not correctly formatted
After some digging around, i found THIS article that helped me get a solution:
Client.OnMessage((receivedMessage) =>
{
var bodyJson = new StreamReader(receivedMessage.GetBody<Stream>(), Encoding.UTF8).ReadToEnd();
var myMessage = JsonConvert.DeserializeObject<MyMessage>(bodyJson);
});
If anyone has faced this issue and found a better solution, please let me know!
Thanks!
To anyone who found this question if they were getting this error from sending the message using Service Bus Explorer (like me).
Make sure you specify the correct message type in the drop down:
Thanks for the update, I was doing the reverse and this helped me. I thought I'd add to your solution for completeness. The DeserializeObject method needs the "MyMessage" class defining. In your original post, your JSON is:
{ foo: 'Bar' }
If we drop that into json2csharp (json2csharp.com) we now have the class required to complete your solution:
public class MyMessage
{
public string foo { get; set; }
}
Of course, the dependency is having Newtonsoft.Json package added to your Visual Studio solution:
Install-Package Newtonsoft.Json -Pre
Using the nuget package: Microsoft.Azure.ServiceBus
The following info is contained inside as as comment:
If a message is only being sent and received using this Microsoft.Azure.ServiceBus
client library, then the below extension methods are not relevant and should not be used.
If this client library will be used to receive messages that were sent using both WindowsAzure.Messaging client library and this (Microsoft.Azure.ServiceBus) library, then the Users need to add a User property Microsoft.Azure.ServiceBus.Message.UserProperties while sending the message. On receiving the message, this property can be examined to determine if the message was from WindowsAzure.Messaging client library and if so use the message.GetBody() extension method to get the actual body associated with the message.
---------------------------------------------- Scenarios to
use the GetBody Extension method: ----------------------------------------------
If message was constructed using the WindowsAzure.Messaging client library as
follows:
var message1 = new BrokeredMessage("contoso"); // Sending a plain string var
message2 = new BrokeredMessage(sampleObject); // Sending an actual customer object
var message3 = new BrokeredMessage(Encoding.UTF8.GetBytes("contoso")); // Sending
a UTF8 encoded byte array object await messageSender.SendAsync(message1); await
messageSender.SendAsync(message2); await messageSender.SendAsync(message3);
Then retrieve the original objects using this client library as follows: (By
default Microsoft.Azure.ServiceBus.InteropExtensions.DataContractBinarySerializer
will be used to deserialize and retrieve the body. If a serializer other than
that was used, pass in the serializer explicitly.)
var message1 = await messageReceiver.ReceiveAsync(); var returnedData1 = message1.GetBody();
var message2 = await messageReceiver.ReceiveAsync(); var returnedData2 = message1.GetBody();
var message3 = await messageReceiver.ReceiveAsync(); var returnedData3Bytes =
message1.GetBody(); Console.WriteLine($"Message3 String: {Encoding.UTF8.GetString(returnedData3Bytes)}");
------------------------------------------------- Scenarios to NOT use the GetBody
Extension method: ------------------------------------------------- If message
was sent using the WindowsAzure.Messaging client library as follows: var message4
= new BrokeredMessage(new MemoryStream(Encoding.UTF8.GetBytes("contoso"))); await
messageSender.SendAsync(message4); Then retrieve the original objects using this
client library as follows: var message4 = await messageReceiver.ReceiveAsync();
string returned = Encoding.UTF8.GetString(message4.Body); // Since message was
sent as Stream, no deserialization required here.
May it help you
With the latest Service Bus client libraries (.NET, JS, Java, Python), you can send message(s) using the JS library like this:
const serviceBusClient = new ServiceBusClient("<connectionstring>");
const sender = serviceBusClient.createSender("<queuename>");
await sender.sendMessages([{
body: {
title: "hello"
}
}]);
Note that .sendMessages takes a list as an input, even if you're just sending one message.
And get the body of the received message using .NET library like this:
await using var client = new ServiceBusClient("<connectionstring>");
ServiceBusReceiver receiver = client.CreateReceiver("<queuename>");
ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
string body = receivedMessage.Body.ToString();
Console.WriteLine(body); //prints {"title":"hello"}

Categories