Call External Application from within Epicor

I’m trying to make an external call to UPS’s API to do address validation. I’m able to get this working within a Visual Studio app, but am having problems getting that converted into Epicor. Just to prove out the concept, I’m trying to add this into a UD Table Maintenance program, add a button, and get it to call. Once I get the concept proven out, I’ll then take that logic an incorporate this into other programs, such as shipment entry, order entry, customer maintenance, etc.

The VS app where I’m running it uses Json and HTTP services. I was able to get the code in my UD Table Maint program to handle the Json code (by adding a reference to Newtonsoft.Json.dll). However, I can’t seem to get the correct references/usings to get the HTTPClient logic working.

After spending much time digging into this, mainly via this forum, I’m under the impression that we really don’t want to use HTTPClient webservice calls anyway, and should be connecting to the external application via Rest (specifically, restsharp?). But, before investing the time in converting the code from httpClient calls to rest calls, I want to ensure I can get the correct environment set up (custom references, using statements, etc). I was hoping to find a snippet of code where someone’s done something similar. If I had hair to pull out, I’d be there now.

Am I heading in the right direction? Any help in pointing me in the right direction would be very helpful.

Hello,

I am working on something similar. Here is roughly what I did to call an external REST API from inside an Epicor Function. I plan to call the Epicor Function from a customization (Customer Shipment Entry) when the user clicks a button. I have not figured out the customization part yet, but I have been testing the Epicor Function by itself (via REST API) and it works. The snippet below is a reduced case, but I’m doing more complex GET and POST with JSON in the body (Newtonsoft.Json) and LINQ in my application. I am not an expert and I don’t know if this is the best/correct approach, but hopefully it helps.

using System.Net;
using System.Text;
using System.IO;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

string url = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Accept = "application/xml";
request.ContentType = "application/json";
response = (HttpWebResponse)request.GetResponse();
responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
response.Close();

var document = XDocument.Parse(responseString);

3 Likes

An HTTP Client can call a RESTful API service, try not to get those terms confused. Think of the Http client as just an interface that is used to call the API. This is a very lightweight way to integrate to systems.
RestSharp is an HTTP client, but you can use whatever you want. Epicor ships with the HTTPWebRequest class as part of the System.net namespace. Here’s an example of calling an API (in this case it’s calling an Epicor Function) endpoint with the HttpWebRequest class out of a client side customization. This is a good example if you want to use out of the box references.


private void epiButtonC1_Click(object sender, System.EventArgs args)
	{
		//API Key is included in the query param in this example. 
		var request = (HttpWebRequest)WebRequest.Create("https://server/Epicor10DEV/api/v2/efx/Company/TestLibrary/function-1/?api-key=yourAPIKeyHere");
		request.Method = "POST";
		//All REST v2 requests also sent with authentication method (Token, Basic)
		//This should be Base64 encoded
		string username = "username";
		string password = "password";
		string encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
		request.Headers.Add("Authorization", "Basic " + encoded);
		
		//Add body to correspond to request signature
		request.ContentType = "application/json";
		using(var writer = new StreamWriter(request.GetRequestStream()))
		 {
		    var values = new Dictionary<string, string>
		      {
		        {"toEmailAddress", "toEmail@email.com"},
		        {"fromEmailAddress",fromEmail@email.com"}, 
		        {"body","This is the body"},   
				{"subject","Hello from Client Code!"}
		    };
	    string json = JsonConvert.SerializeObject(values);
	    writer.Write(json);	
		}	
		using (var response = request.GetResponse()) 
		using (var reader = new StreamReader(response.GetResponseStream()))
		{
		  var result = reader.ReadToEnd();
		  epiTextBoxC1.Text = result.ToString();
		}

	}

5 Likes

That was very helpful Aaron. I was able to use your code and get the logic working. I’m having an issue now connecting that I’m digging into. I’m now getting these messages:

Message: The underlying connection was closed: An unexpected error occurred on a send.
Inner Exception Message: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

I added security protocol to the program, but still getting the same error. My code is here

string url = "https://onlinetools.ups.com/addressvalidation/v1/1?maximumcandidatelistsize=1";
var webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.Headers.Add("AccessLicenseNumber", accessLicenseNumber);
webRequest.Headers.Add("Username", username);
webRequest.Headers.Add("Password", password);
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] byteArray = encoding.GetBytes(JsonConvert.SerializeObject(ExternalToInternalAddressRequestConverter.Convert(addressRequest)));
webRequest.ContentType = "application/json; charset=UTF-8";
webRequest.ContentLength = byteArray.Length;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Stream dataStream = webRequest.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();

Calling it a night. I’ll give it a further go tomorrow. Thanks for your help.

You might try calling the ups api in something like Postman first to work out any kinks such as the one you’re getting. They might have deprecated that protocol (just a guess) but very hard to say without knowing more about the response you’re getting.

Sounds like you’re on the right track at any rate! These things are always a lot of fun to debug at first :wink:

Edit: what version of .net is the machine running?

How were you able to get the Newtonsoft.Json reference to validate on check syntax?

I believe I had to download the newtonsoft files from their webiste, and dropped it into a folder I created on the server folder. I then added that just like I would any other reference (add custom reference). I might have had to add a “using” statement as well.

I think the current versions of Epicor include the newtonsoft files already.