Reputation: 327
I have created a custom action that takes in an input parameter called paramsInput
(string).
I want to send to said action a stringified JSON object so that it may be deserialized inside the action.
Snippet of action code:
public void Execute(IServiceProvider serviceProvider)
{
try
{
_context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
_crmService = factory.CreateOrganizationService(_context.UserId);
string paramsInput = _context.InputParameters.Contains("paramsInput") ? _context.InputParameters["paramsInput"].ToString() : string.Empty;
if (paramsInput != string.Empty)
{
// Deserialize to concrete class
InputParams inputParameters = JsonConvert.DeserializeObject<InputParams>(paramsInput);
// logic here...
}
}
catch (Exception ex)
{
// handle exception...
}
}
I have also created a generic function that receives the action's name and the parameters to send and calls the action via OData:
public bool CallMessage(string action, JObject parameters, out string reason)
{
RegenerateAccess(); // Microsoft Online
string urlAPI = "/api/data/v9.1/" + action;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(_serviceUrl);
client.Timeout = new TimeSpan(0, 2, 0); //2 minutes
client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
client.DefaultRequestHeaders.Add("OData-Version", "4.0");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, urlAPI);
/*StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
using (JsonTextWriter writer = new JsonTextWriter(sw))
{
writer.QuoteChar = '\'';
JsonSerializer ser = new JsonSerializer();
ser.Serialize(writer, parameters);
}*/
var jsonObj = JsonConvert.SerializeObject(parameters);
//request.Content = new StringContent(sb.ToString(), Encoding.UTF8, "application/json");
request.Content = new StringContent(jsonObj, Encoding.UTF8, "application/json");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
//Set the access token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authResult.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
reason = response.Content.ReadAsStringAsync().Result;
return response.IsSuccessStatusCode;
}
}
My issue is that I'm getting a Bad Request 400
back, and the response reads - Microsoft.OData.ODataException: An unexpected 'StartObject' node was found for property named 'paramsInput' when reading from the JSON reader. A 'PrimitiveValue' node was expected.
My parameters JObject is structured as follows:
{
"paramsInput" : {
"id" : "xxxx-xxxx...", // some guid
"code" : 1 // int
}
}
or via code:
Guid guid = Guid.NewGuid();
JObject parameters = new JObject();
parameters.Add("id", guid);
parameters.Add("code", 1);
JObject wrapper = new JObject();
wrapper.Add("paramsInput", parameters);
// send wrapper JObject to CallMessage
I don't want to change the structure of CallMessage function as it is a generic function that may be used multiple times by other sources.
Upvotes: 0
Views: 223
Reputation: 186
Henk's answer is correct, You told the API to accept a string, thus you must send it the serialized version of your JSON in the string parameter.
I cannot add a comment to Henk's answer due to length restrictions, but I wanted to add a point of clarification here:
The Action is a bit of legacy nowadays and has been replaced by the Custom API. A Custom API pretty much works in the same way and as a bonus you do not need to create custom plugin steps.
You don't need to create a plugin supporting it if you're going to process it somewhere else ( Like PowerAutomate). This is technically called an "Event" in the Power Platform. You also don't need to create a plugin to support a custom action.
The fundamental difference is that "Custom Action" was implemented as a Workflow Operation, where "CustomAPI" is not. You gain a bit of perf as the system does not need to invoke the workflow runtime to run your operation.
Originally, 'Custom Action' was intended to be used as a custom workflow implementation (Think the way you use an HTTP trigger for PowerAutomate today.) However, folks quickly realized that you could wire up a Plugin to the SDK message to get it to act like a 'custom api' inside Dataverse.
Carrying that forward, the CustomAPI Definition is used for both Events and Custom Operations.
As Henk Said, 'Custom Action' is the older process for creating a callable interface for this.
Upvotes: 0
Reputation: 7918
The action's parameter paramsInput
(containing the JSON) is a string
parameter. Therefore, when this parameter is constructed, it must be stringified and your last code snippet would need to look like this:
Guid guid = Guid.NewGuid();
JObject parameters = new JObject();
parameters.Add("id", guid);
parameters.Add("code", 1);
JObject wrapper = new JObject();
wrapper.Add("paramsInput", JsonConvert.SerializeObject(parameters));
// send wrapper JObject to CallMessage
A few side notes:
HttpClient
, which in itself is fine, but doing so you need to do all plumbing yourself. Another option would be to use the OrganizationRequest
from the MS SDK libraries.Upvotes: 1