@klincecum, I’ve been able to get past the Function issue, but I’m still having a problem calling the function within C#. Below is thte code I’m using in the call:
private static async void CallPostFunction()
{
try
{
string ApiKey = Properties.Settings.Default.APIKey;
var RestClient = new RestClientBuilder()
.SetAppServerUrl(Properties.Settings.Default.AppServerURL)
.SetCompanyId(Properties.Settings.Default.CompanyID)
.SetDefaultApiKey(ApiKey)
.UseBasicAuthentication(Properties.Settings.Default.BatchEntryPerson, Properties.Settings.Default.BatchEntryPass)
.Build();
var FunctionParameters = new RestContent(new { GroupID = BatchGroupID });
var FunctionResponse = await RestClient.Function.PostAsync(Properties.Settings.Default.RESTLibrary, Properties.Settings.Default.RESTFunction, FunctionParameters, true);
if (FunctionResponse.IsSuccessStatusCode != true)
{
Console.WriteLine(FunctionResponse.StatusCode.ToString() + " " + FunctionResponse.Content);
}
}
catch (Exception e)
{
throw new Exception("REST Call", e);
}
When the call is executed, I get a System.IO.IOException from System.Private.CoreLib.dll, then a System.Exception from the project. I can eexcute the same call from Swagger with no error, I just don’t know what I’m doing wrong in the code. Would you have a minute or two to help me?
…OK, what would I use instead? I HAD this working before upgrading this project to .NET 7 and VS 2022. Is there any way to revert a project backward without completely rebuilding it?
Kevin, do you have an example of using RestSharp to call the function using an API Key and a parameter? I’ve been looking for one but haven’t had any luck in finding one specific to Epicor.
Thank you! I apologize for being a pest, this is all just VERY new to me, and I have a project that is almost past deadline to get this working. BTW, any suggestion on reading to get up to speed on using REST calls? Or just the Epicor REST V2 documentation?
To be honest, I recommend doing a search here for “REST”, clicking “More…”, and
read whatever you see that looks interesting. (Or everything, if you’re like me )
Kevin, hopefully one last issue… I installed RestSharp 110.2.0 in my project and used the example from the link above to code a POST call to my Epicor function. Below is the code I created and a Swagger page showing the function call I’m trying to make. The function call works fine in Swagger, however it’s failing without any error when calling from the RestSharp code. Do I need to deserialize the argument data in the request to call the function, or is there something else I have screwed up?
private static void CallPostFunction()
{
try
{
var options = new RestClientOptions(Properties.Settings.Default.AppServerURL);
options.Authenticator = new HttpBasicAuthenticator(Properties.Settings.Default.BatchEntryPerson, Properties.Settings.Default.BatchEntryPass);
var client = new RestClient(options);
var request = new RestRequest();
request.AddHeader("Accept", "application/json");
request.AddHeader("X-API-Key", Properties.Settings.Default.APIKey);
request.Method = Method.Post;
string RequestContent = "\"GroupID\":" + $@"{BatchGroupID}";
request.AddParameter("application/json", RequestContent, ParameterType.RequestBody);
var response = client.Execute(request);
}
catch (Exception e)
{
throw new Exception("REST Call", e);
}
}
Kevin, I built the JSON object and serialized it like you said, then ran my debug. However, when I try to execute the POST call to the server I’m getting the following error:
The SSL connection could not be established. See inner exception for details.
Name Value Type
▶ InnerException {"The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot"} System.Exception {System.Security.Authentication.AuthenticationException}
at System.Net.Http.ConnectHelper.<EstablishSslConnectionAsync>d__2.MoveNext() at System.Threading.Tasks.ValueTask`1.get_Result() at System.Net.Http.HttpConnectionPool.<ConnectAsync>d__100.MoveNext() at System.Threading.Tasks.ValueTask`1.get_Result() at System.Net.Http.HttpConnectionPool.<CreateHttp11ConnectionAsync>d__102.MoveNext() at System.Threading.Tasks.ValueTask`1.get_Result() at System.Net.Http.HttpConnectionPool.<AddHttp11ConnectionAsync>d__76.MoveNext() at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1.MoveNext() at System.Threading.Tasks.ValueTask`1.get_Result() at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.<WaitForConnectionAsync>d__5.MoveNext() at System.Net.Http.HttpConnectionPool.<SendWithVersionDetectionAndRetryAsync>d__86.MoveNext() at System.Threading.Tasks.ValueTask`1.get_Result() at System.Net.Http.RedirectHandler.<SendAsync>d__4.MoveNext() at System.Threading.Tasks.ValueTask`1.get_Result() at System.Net.Http.DecompressionHandler.<SendAsync>d__16.MoveNext() at System.Net.Http.HttpClient.<<SendAsync>g__Core|83_0>d.MoveNext() at RestSharp.RestClient.<ExecuteRequestAsync>d__3.MoveNext()
Couple of questions:
Can I use a self-signed certificate?
Where on the server does it need to be located?
Where in the project do I need to indicate where and what certificate is to be used?
When you installed your appServer, you had the opportunity to choose a certificate or let Kinetic create one for you. You can see these certs in certmgr at the server.
To check binding, use IIS Manager and select bindings:
To automate all of this, you can follow @jgiese.wci and @EarlGrei’s “stupid simple” advice and use a service that generates free certs for you. Or if you want to do it yourself with a little help from Azure (yes, even for on-prem servers) you can read the following. Using real trusted certs just makes life so much more simple.
Kevin, I found a certificate that IIS is using on the server to which I’m issueing the REST call that is not expired. To verify the certificate like you indicated, where would I find the hash value? I have a screenshot of the certificate details below, would it be the Thumbprint or Serial Number value?
Kevin, the code snippet isn’t working, I think due to the version I’m using (110.2.0). VS is saying the RemoteCertificateValidationCallback is under the RestClientOptions class, so I set up the code like this:
var options = new RestClientOptions(Properties.Settings.Default.AppServerURL)
{
Authenticator = new HttpBasicAuthenticator(Properties.Settings.Default.BatchEntryPerson, Properties.Settings.Default.BatchEntryPass)
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
string certHash = "";
certHash = certificate.GetCertHashString(System.Security.Cryptography.HashAlgorithmName.SHA512);
Console.WriteLine(certHash);
}
};