Receiving BrokeredMessage from ServiceBus from a nodejs server - node.js

I am reading messages from an existing Azure ServiceBus from a new nodejs Server.
This is the way the messages are being sent to the ServiceBus from a .NET server:
var topicClient = TopicClient.CreateFromConnectionString(serviceBusConnectionString, routingKey);
var brokeredMessage = new BrokeredMessage(message.ToJson());
topicClient.Send(brokeredMessage);
Where topicClient is Microsoft.ServiceBus.Messaging.TopicClient
I am using the following method to read the messages on a nodejs server using azure-sb package:
sbClient = ServiceBusClient.createFromConnectionString(connectionString)
subscriptionClient = this.sbClient.createSubscriptionClient(topicName, subscriptionName);
receiver = this.subscriptionClient.createReceiver(ReceiveMode.receiveAndDelete);
messages = await this.receiver.receiveMessages(10,10);
console.log(messages.map(message => { message.body }));
message.body is a buffer and when I do message.body.toString('utf-8') I get something like:
#string3http://schemas.microsoft.com/2003/10/Serialization/��{VALID JSON}
I am interested of course in the valid JSON in between.
In the .net servers we simply do brokeredMessage.GetBody() and we get the object, so is there an easy way on nodejs to do the same?

According to my test, if we use the standard library Microsoft.Azure.ServiceBus to send messages in .Net application, it will directly JSON parse the message in node application
For example
This is my C# code to send a message:
class Program
{
static void Main(string[] args)
{
string connectionString = "Endpoint=sb://...";
var client = new TopicClient(connectionString, "");
var payload = JsonConvert.SerializeObject(new DemoMessage() { Title = $"hello!!! {DateTime.Now}" });
var serviceBusMessage = new Message(Encoding.UTF8.GetBytes(payload));
serviceBusMessage.SessionId = Guid.NewGuid().ToString("D");
client.SendAsync(serviceBusMessage).Wait();
}
private class DemoMessage
{
public DemoMessage()
{
}
public string Title { get; set; }
}
This is my Node.js code to receive a message:
const { ServiceBusClient, ReceiveMode } = require("#azure/service-bus");
// Define connection string and related Service Bus entity names here
const connectionString =
"Endpoint=sb://";
const topicName = "***";
const subscriptionName = "***";
async function main() {
const sbClient = ServiceBusClient.createFromConnectionString(
connectionString,
);
const subscriptionClient = sbClient.createSubscriptionClient(
topicName,
subscriptionName,
);
const receiver = subscriptionClient.createReceiver(ReceiveMode.receiveAndDelete);
try {
const messages = await receiver.receiveMessages(1);
console.log("Received messages:");
console.log(messages.map((message) => message.body));
await subscriptionClient.close();
} finally {
await sbClient.close();
}
}
main().catch((err) => {
console.log("Error occurred: ", err);
});
Besides, if you still use the library WindowsAzure.ServiceBus, we need to use BrokeredMessage(Stream messageBodyStream, bool ownsStream) to initialize an object.
Because we use BrokeredMessage(platload) to initialize, it will use DataContractSerializer with a binary XmlDictionaryWriter to initialize an object. So, the payload is being serialized using DataContractSerializer with a binary XmlDictionaryWriter and this is the cause that explains why the body of the message has in its start the type indication #string3http://schemas.microsoft.com/2003/10/Serialization/.
For example
This is my C# code to send a message:
var client =TopicClient.CreateFromConnectionString(connectionString, "test");
var payload = JsonConvert.SerializeObject(new DemoMessage() { Title = $"hello BrokeredMessage!!! {DateTime.Now}" });
using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(payload))) {
var serviceBusMessage = new BrokeredMessage(stream,true);
await client.SendAsync(serviceBusMessage);
}
I use the same code to receive
For more details, please refer to here.

Related

calling .net core 3.1 gRpc service from nodejs client

Using .net core 3.1 on windows 10. Created sample GrpcGreeter server and GrpcGreeterClient applications. From GrpcGreeterClient app, I can successfully call Greeter service.
greet.proto
syntax = "proto3";
option csharp_namespace = "GrpcGreeterClient";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
client code;
static async Task Main(string[] args)
{
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
So far so good. However, when I try to call this same gRpc service from nodejs receved an error.
client proto file used in nodejs;
syntax = "proto3";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
nodejs code in typescript;
let PROTO_PATH = path.resolve(__dirname, "../../proto/Greet.proto");
let packageDefinition = protoLoader.loadSync(PROTO_PATH);
const grpcObj = grpc.loadPackageDefinition(
packageDefinition
) as unknown as ProtoGrpcType;
const enmPackage = grpcObj.greet;
let client = new enmPackage.Greeter(
"localhost:5001",
grpc.credentials.createInsecure()
);
client.SayHello(
{
name: "Joe",
},
(err, reply) => {
if (err) {
console.error(err);
} else {
console.log(reply);
}
}
);
When I call client.SayHello, I received Error: 14 UNAVAILABLE: Connection dropped
What am I missing? Should I add any option in proto file?

NodeJS - #azure/service-bus : How to pass `userProperties` in message to C# backend

Before, I was using azure-sb package to handle service bus message in NodeJS with below sample code:
let message = {
body: JSON.stringify(body),
customProperties: {
userId: userId
}
};
However, after changed to use package #azure/service-bus, I needed to change a little bit to get body in C# code as below:
let signMessage = {
body: body,
customProperties: { // tried to use userProperties but not okay
userId: userId
}
};
However, I still cannot get userProperties successfully in C# or ServiceBus Explorer.
Simple code:
const { ServiceBusClient } = require("#azure/service-bus");
const connectionString = "Endpoint=sb://bowman1012.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxxxxx"
const topicName = "test";
const messages = [
{ body: "Albert Einstein",
applicationProperties: {
userId: 'userId'
}
}
];
async function main() {
// create a Service Bus client using the connection string to the Service Bus namespace
const sbClient = new ServiceBusClient(connectionString);
// createSender() can also be used to create a sender for a queue.
const sender = sbClient.createSender(topicName);
try {
// Tries to send all messages in a single batch.
// Will fail if the messages cannot fit in a batch.
// await sender.sendMessages(messages);
// create a batch object
let batch = await sender.createMessageBatch();
for (let i = 0; i < messages.length; i++) {
// for each message in the arry
// try to add the message to the batch
if (!batch.tryAddMessage(messages[i])) {
// if it fails to add the message to the current batch
// send the current batch as it is full
await sender.sendMessages(batch);
// then, create a new batch
batch = await sender.createBatch();
// now, add the message failed to be added to the previous batch to this batch
if (!batch.tryAddMessage(messages[i])) {
// if it still can't be added to the batch, the message is probably too big to fit in a batch
throw new Error("Message too big to fit in a batch");
}
}
}
// Send the last created batch of messages to the topic
await sender.sendMessages(batch);
console.log(`Sent a batch of messages to the topic: ${topicName}`);
// Close the sender
await sender.close();
} finally {
await sbClient.close();
}
}
// call the main function
main().catch((err) => {
console.log("Error occurred: ", err);
process.exit(1);
});
It works fine on my side.
This is the API reference:
https://learn.microsoft.com/en-us/javascript/api/#azure/service-bus/servicebusmessage?view=azure-node-latest

Dialogflow Webhook Response c# gives error on invoke

I'm trying to create a webhook for Dialogflow in C# (on Azure). Everytime I see the same example but my DialogFlows keeps geting an error with this response"
Here's what I did:
Created a new ASP.Net Web Project (WebAPI)
installed NuGet Google.Cloud.DialogFlow V2 (v1.0.0.beta02)
updated System.Net.Http to 4.3.3
Created a new controller
[System.Web.Http.HttpPost]
public dynamic DialogAction([FromBody] WebhookRequest dialogflowRequest)
{
var intentName = dialogflowRequest.QueryResult.Intent.DisplayName;
var actualQuestion = dialogflowRequest.QueryResult.QueryText;
var testAnswer = $"Dialogflow Request for intent {intentName} and question {actualQuestion}";
var parameters = dialogflowRequest.QueryResult.Parameters;
var dialogflowResponse = new WebhookResponse
{
FulfillmentText = testAnswer,
FulfillmentMessages =
{ new Intent.Types.Message
{ SimpleResponses = new Intent.Types.Message.Types.SimpleResponses
{ SimpleResponses_ =
{ new Intent.Types.Message.Types.SimpleResponse
{
DisplayText = testAnswer,
TextToSpeech = testAnswer,
}
}
}
}
}
};
var jsonResponse = dialogflowResponse.ToString();
return new ContentResult
{
Content = jsonResponse,
ContentType = "application/json"
};
Published the app to Azure so there's a webhook URl.
Now, when I test it in dialogflow, the response is:
"Webhook call failed. Error: Failed to parse webhook JSON response: Cannot find field: Content in message google.cloud.dialogflow.v2.WebhookResponse."
Which I do not understand.....what am I missing here?
(here's the screenshot of the response:)
The solution to this problem is to return JsonResult instead of the ContentResult.
[System.Web.Http.HttpPost]
public JsonResult DialogAction([FromBody] WebhookRequest dialogflowRequest)
{
var intentName = dialogflowRequest.QueryResult.Intent.DisplayName;
var actualQuestion = dialogflowRequest.QueryResult.QueryText;
var testAnswer = $"Dialogflow Request for intent {intentName} and question {actualQuestion}";
var parameters = dialogflowRequest.QueryResult.Parameters;
var dialogflowResponse = new WebhookResponse
{
FulfillmentText = testAnswer,
FulfillmentMessages =
{ new Intent.Types.Message
{ SimpleResponses = new Intent.Types.Message.Types.SimpleResponses
{ SimpleResponses_ =
{ new Intent.Types.Message.Types.SimpleResponse
{
DisplayText = testAnswer,
TextToSpeech = testAnswer,
}
}
}
}
}
};
var jsonResponse = dialogflowResponse.ToString();
return Json(jsonResponse);

Node js client for grpc server

I have GRPC server running using openssl - static way and I am trying to connect to server using nodejs client
I do not see any error but I do not see its connecting to server either.
Please share if you have any sample.
Please refer code below:
var rootCertPath = path.join('.','.', 'server-root.PEM');
var privateCertPath = path.join('.','.', 'server-private.PEM');
var domainCertPath = path.join('.','.', 'server-domain.PEM');
var rootCert = fs.readFileSync(rootCertPath);
var privateCert = fs.readFileSync(privateCertPath);
var domainCert = fs.readFileSync(domainCertPath);
var buf1 = new Buffer('rootCert');
var buf2 = new Buffer('privateCert');
var buf3 = new Buffer('domainCert');
var chat_proto = grpc.load("Chat.proto").com.company.grpc;
var client = new chat_proto.ChatService('https://servervip:443',grpc.credentials.createSsl(buf1,buf2,buf3));
Chat.proto
syntax = "proto3";
// Service definition.
service ChatService {
// Sends a chat
rpc chat(stream ChatMessage) returns (stream ChatMessageFromServer) {}
}
// The request message containing the user's name.
message ChatMessage {
string name = 1;
string message = 2;
}
// The response message containing the greetings
message ChatMessageFromServer {
string name = 1;
string message = 2;
}
//Code to make a request
var username = process.argv[2];
var stdin = process.openStdin();
function main() {
console.log("starting");
console.log(client); // prints { '$channel': Channel {} }
var chat=client.chat();
chat.on('data', function(msg) {
console.log(msg.name + ': ' + msg.message);
console.log("after message");
});
stdin.addListener('data',function(input) {
chat.write({ name: username, message: input.toString().trim()
});
});
}
main();
so good new is - below thing worked for me
var rootCertPath = path.join('.','.', 'roots.PEM');
var rootCert = fs.readFileSync(rootCertPath);
var chat_proto = grpc.load("Chat.proto").com.americanexpress.grpc.chat;
var client = new chat_proto.ChatService('servervip:443',grpc.credentials.createSsl(rootCert));
Looks like an issue with the cert - I used the default roots.PEM in grpc client and it worked for me. will look internally to have correct root of my servervip CA certificate chain.
Thanks all for your support

Uncompress gzipped http request body to json in Node.js

I have a windows 8 application connecting to a web service written in Node.js. On the windows 8 side I compressed my request body to gzip. But on the Node.js side I found that my req.body type was Object.
I cannot use zlib to uncomporess the body since it's not a stream.
I can use zlib to uncomporess the req, but I don't know how to retrieve the req.body content from the unzipped stream and parse the body in JSON format.
BTW, I reviewed my request through Fiddler and it told me the request body was gzipped, and I can see my raw body through Fiddler after unzipped so the request should be correct.
Updated
Below is my Node.js app
(function () {
var express = require("express");
var zlib = require("zlib");
var app = express();
var port = 12345;
app.configure(function () {
app.use(express.compress());
app.use(express.bodyParser());
});
app.post("/test", function (req, res) {
var request = req.body;
req.pipe(zlib.createGunzip());
var response = {
status: 0,
value: "OK"
};
res.send(200, response);
});
console.log("started at port %d", port);
app.listen(port);
})();
And below is my windows store app code (partial)
private async void button1_Click_1(object sender, RoutedEventArgs e)
{
var message = new
{
Name = "Shaun",
Value = "12345678901234567890123456789012345678901234567890"
};
var json = await JsonConvert.SerializeObjectAsync(message, Formatting.Indented);
var bytes = Encoding.UTF8.GetBytes(json);
var client = new HttpClient();
client.BaseAddress = new Uri("http://192.168.56.1:12345/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.ExpectContinue = false;
var jsonContent = new JsonContent(message);
var gzipContent = new GZipContent3(jsonContent);
var res = await client.PostAsync("test", gzipContent);
var dialog = new Windows.UI.Popups.MessageDialog(":)", "完成");
await dialog.ShowAsync();
}
internal class GZipContent3 : ByteArrayContent
{
public GZipContent3(HttpContent content)
: base(LoadGZipBytes(content))
{
//base.Headers.ContentType = content.Headers.ContentType;
base.Headers.ContentType = new MediaTypeHeaderValue("x-application/x-gzip");
base.Headers.ContentEncoding.Add("gzip");
}
private static byte[] LoadGZipBytes(HttpContent content)
{
var source = content.ReadAsByteArrayAsync().Result;
byte[] buffer;
using (var outStream = new MemoryStream())
{
using (var gzip = new GZipStream(outStream, CompressionMode.Compress, true))
{
gzip.Write(source, 0, source.Length);
}
buffer = outStream.ToArray();
}
return buffer;
}
}
internal class JsonContent : StringContent
{
private const string defaultMediaType = "application/json";
public JsonContent(string json)
: base(json)
{
var mediaTypeHeaderValue = new MediaTypeHeaderValue(defaultMediaType);
mediaTypeHeaderValue.CharSet = Encoding.UTF8.WebName;
base.Headers.ContentType = mediaTypeHeaderValue;
}
public JsonContent(object content)
: this(GetJson(content))
{
}
private static string GetJson(object content)
{
if (content == null)
{
throw new ArgumentNullException("content");
}
var json = JsonConvert.SerializeObject(content, Formatting.Indented);
return json;
}
}
http://www.senchalabs.org/connect/json.html. Basically you need to write your own middleware based on connect.json() that pipes through an uncompression stream like connect.compress() but the opposite direction: http://www.senchalabs.org/connect/compress.html
Also, make sure you're sending the correct Content-Encoding header in your request.
If you show me what you have so far I can help you further.
I was working on similar thing and finally landed on
function getGZipped(req, callback) {
var gunzip = zlib.createGunzip();
req.pipe(gunzip);
var buffer = [];
gunzip.on('data', function (data) {
// decompression chunk ready, add it to the buffer
buffer.push(data);
}).on('end', function () {
//response and decompression complete, join the buffer and return
callback(null, JSON.parse(buffer));
}).on('error', function (e) {
callback(e);
});
}

Resources