How to post a c# Hashtable with nodejs to WCF (basichttpbinding) - c#

I have to access a WCF with node js. In this WCF there is a function that has one parameter which is an Hashtable. I'm doing this by using the SOAP protocol.
I'm wondering if it's possible to do this without using XML. Simple variables types like integer, string, bool etc. are sent fine, but the Hashtable is not accepted.
var soap = require('soap');
var args = {
account_id: 'xxxxx',
username: 'xxxxxx',
password: 'xxxxx',
dotComLogin: false,
_hashtableArg: {
a: 'aaaa',
b: 'bbbb',
},
offset: 5,
offsetIterations: 10
};
var url = "http://localhost:55791/WCF.svc?wsdl";
soap.createClient(url, function(err, client) {
client.requestJobId(args, function(err, result) {
console.log(result);
});
});
I know that I can use the wcf.js module for NodeJs, but I don't want to write XML SOAP messages. Is this possible?

Related

NServicebus receive messages without all the NServicebus specific stuff

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"}
}
);

Is it possible to call an Dynamics CRM action with a JSON request through a WebApi with IOrganizationService?

TL;DR:
I am calling a WebApi, the WebApi authenticates against the CRM and use the IOrganizationService, so my request is a JObject and not an Entity or EntityReference, it gives me this error:
Error: Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.
Context:
I built a web application in angular and I built a WebApi so I can call some custom actions in CRM:
Angular APP | WebApi | OnPremise CRM
So, when I call the WebApi, there is a controller that turns my request into a OrganizationRequest:
Request for WebApi:
{
"ActionName": "custom_actionname",
"Parameters":
[
{
"Key": "EntityInputParameter1",
"Value": {"#odata.type":"Microsoft.Dynamics.CRM.any_entity"}
}
]
}
I read this request on my WebApi and turn that into a request for CRM
Request for CRM:
OrganizationRequest request = new OrganizationRequest("custom_actionname");
request.Parameters["EntityInputParameter1"] = {"#odata.type":"Microsoft.Dynamics.CRM.any_entity"} // This is a JObject
OrganizationResponse response = service.Execute(request);
When I make the request, it gives me the following error:
Error: Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.
If I make the request directly to the action it works, but I cannot do that due security policies.
One option could be turn the request into a valid CRM request (parsing {"#odata.type":"Microsoft.Dynamics.CRM.any_entity} into a Entity type) but CRM has a lot of parsing escenarios and could be very complex.
Another option could be sending the request through web and stop using the IOrganizationService but I cannot change that.
I am making this question so anybody that has this error can find the "solution" because I searched a lot and nobody refers this behavior directly.
I am probably turning my InputEntityParameter into string, and I will send the JSON, so I can parse the JSON on my action, but I was looking if anybody else had this error or another approach.
I tested it on one of my Dev Environment with Entity as Parameter.
Below is the code I used in console application to fire Action with Entity as parameter. It ran successfully
var request = new OrganizationRequest("new_test");
//request.Parameters.Add("Target", xAccountReference);
request.Parameters.Add("Param2", "abc");
request.Parameters.Add("Param1", new Entity("account",Guid.Parse("2fe32f22-d01d-ea11-80fa-005056936c69")));
Service.Execute(request);
Below is the Javascript code which used CRM Webapi to execute Action with Parameter. Ignore the XRM.Webapi command but interesting for you would be passing parameters in webapi.
var parameters = {};
parameters.Param2 = "abcd";
var param1 = {};
param1.accountid = "2fe32f22-d01d-ea11-80fa-005056936c69"; //Delete if creating new record
param1["#odata.type"] = "Microsoft.Dynamics.CRM.account";
parameters.Param1 = param1;
var new_testRequest = {
Param2: parameters.Param2,
Param1: parameters.Param1,
getMetadata: function() {
return {
boundParameter: null,
parameterTypes: {
"Param2": {
"typeName": "Edm.String",
"structuralProperty": 1
},
"Param1": {
"typeName": "mscrm.account",
"structuralProperty": 5
}
},
operationType: 0,
operationName: "new_test"
};
}
};
Xrm.WebApi.online.execute(new_testRequest).then(
function success(result) {
if (result.ok) {
//Success - No Return Data - Do Something
}
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);
I can confirm that you are mixing Webapi and orgservice call. You can definitely call Action from Webapi of Dynamics. I just used Postman to call Action and I was successful. Blog reference to use Postman for CRM webapi
Below Body as json in Postman and I get Action to run.
{
"Param1":"string test",
"Param2":{
"accountid":"b6b35fd0-b9c3-e311-88e2-00505693000c",
"#odata.type":"Microsoft.Dynamics.CRM.account"
}
}

URL Encode form data

My app has a form for users to enter a string of content and a pass-key, which is posted to the server and encrypted. The users are given the encrypted value back. The app allows them to send the encrypted value back to the server with the pass-key and the server will decrypt the data and send it back in the response.
I can successfully post the data for encrypting and get my response back. The issue I have though is that the encrypted data contains characters that need to be URL encoded when the users post the encrypted data back.
This is the Angular 4 service function that requests the encryption.
encrypt(data: CryptoData): Promise<Result> {
let urlParameters = new URLSearchParams();
urlParameters.append('content', data.content);
urlParameters.append('key', data.key);
let body = urlParameters.toString();
return this.http
.post('/api/encrypt', body, this.options)
.toPromise()
.then(response =>
{
return response.json();
});
}
This gets back the encrypted result. Using a Content and Key value of:
content: Foo Bar
key: Test Key
result: AAAAABAAAACUdwXxU1tfClnbpOaKEqNVPuZSxL+cawqTxH+ZAFmDlBAAAAD6o/EFwKE9aDIU1WLOCDtBbY7ERTiKgsXr4pnsvkm+Et+qpJwLORrvPn9QzmJ1uFI=
The result up there is a Base64 string and it contains characters, such as +, that I need to URL encode when the user posts the data back.
What is the angular way to handle this? The backend server is ASP.Net Core and I'm open to changing how my implementation there is done to handle any changes on the Angular side. At the moment I'm posting the data with this function, which causes the + characters to be replaced with spaces.
export class EncryptService implements OnInit {
private options: RequestOptions;
constructor(private http: Http) {
this.options = new RequestOptions({
headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' })
})
}
decrypt(data: CryptoData) {
let urlParameters = new URLSearchParams();
urlParameters.append('content', data.content);
urlParameters.append('key', data.key);
let body = urlParameters.toString();
return this.http
.post('/api/decrypt', body, this.options)
.toPromise()
.then(response =>
{
return response.json();
});
}
}
The interface being passed in looks like this:
export interface CryptoData {
content: string;
key: string;
}
My ASP.Net Core API end-point looks like this:
[HttpPost("~/api/Decrypt")]
public IActionResult Decrypt([FromForm] CryptoRequest request)
{
string content = request.Content;
string key = request.Key;
// .....
return base.Ok();
}
Use base64URL instead on base64.
For Angular, use base64URL.
Run npm install base64url
In your service or component beneath your imports, add the following:
const base64url = require('base64url');
Use like this..
const b64URLresult = base64url(result);
There is an alternative way to add javascript packages using Typings and Definitely Typed.. Didn't work first try for me on this package so gave the above solution.
And in your controller. Use Microsoft.AspNetCore.WebUtilities.Base64UrlDecode

SignalR, passing an object from the HUB to the Client (MVC C#)

I've looked around at other questions but they don't seem to fully answer this question, I'm trying to pass an object via JSON to the client Javascript. I'm using Newtonsoft.Json to make the process easier, but I can't seem to recieve the object.
Here's the code:
When a connection is made, I call the Hub using start().done() in the client javascript:
//start comm with server
$.connection.hub.start().done(function () {
console.log('Grabbing playlist data');
Playlist.server.requestPlaylist();
});
This calls the following method, which is supposed to grab the object and pass it back:
public void requestPlaylist()
{
var playlistData = (from c in db.Playlist where c.ID > 0 select c).Include(h => h.Song).ToList();
Playlist player = new Playlist();
foreach (var item in playlistData)
{
player.ID = item.ID;
player.Profile = item.Profile;
player.Song.ID = item.Song.ID;
player.Song.name = item.Song.name;
player.upvotes = item.upvotes;
}
string jsonObject = JsonConvert.SerializeObject(player);
Clients.All.recievePlaylist(jsonObject);
}
SO here, I'm searching the database, getting the results and storing it into the playlist model, then using newtonsoft.json to convert the model into a json object (Its roughly the same principle they have as an example on their site).
The client javascript that is invoked from this is:
function recievePlaylist(jsonObject) {
console.log('test to recieve data: ' + jsonObject.ID + ' test.');
};
Now just for testing purposes I'm just logging out out to the console, but this come back with nothing:
"test to recieve data: test." is how it comes back.
What am I missing?
Because you convert the object to a string on the server before passing it to the client, the client receives a string. Your string representation of a json object doesnt have an ID property so the value will be "undefined".
On the client you can use this to convert the string to a json object:
jsonObject = JSON.parse(jsonObject);
Just add that line to the top of your recievePlaylist function.
Note: You shouldn't actually need to convert your server object to a json string on the server side. SignalR automatically converts your server side objects to json objects on the client.
If you call WebAPI and receive json response/result on client side (JavaScript/jQuery). The way is general for both SignalR or WebAPI in regards of parse jsone response and the use it as object.
var obj = jQuery.parseJSON( '{ "name": "John" }' );
alert( obj.name === "John" );

how to pass large json string to web service using [webinvoke]

I have a problem passing a large JSON string to a web service from the client side with a jQuery function. When the lenth of the string increases to 1000 characters, the web service throw an exception "Unspecified Network Error". Please let me know how to pass a large JSON value to a web service using [webinvoke].
Can we increase the size of url or whatever to fix this problem?
You can serialize your Json as object:
page.asp
var data1 = {
IdTest: ('<%= miAspVar %>'),
fono: telefono,
etc, etc
};
var strData = YAHOO.lang.JSON.stringify(data1)
callService(testLlamada, strData, EstadoLlamada, onError2);
function onError2(result) {
alert(result);
}
function EstadoLlamada(result) {
...
}

Categories