Search code examples
c#multipartform-dataservicenow-rest-api

POST binary file to ticket ServiceNow REST API - Remove boundary and Content-Type


I am trying to attach a binary file (Excel, MS-Word or Image or anything) to ServiceNow ticket using the POST method available in their REST API.

It works fine when I try to do that using Postman app, however, when I try to do that using C# code, the file gets added successfully however, in the beginning and end of file there is some data due to which files are not valid any more. If I open the attached files using Notepad++, I can see that the file has something like below:

--dc5fc6f1-c907-4a26-b410-1d54256954d6
Content-Disposition: form-data; name=Attachment; filename=Download_14Jul20151332195868.xlsx; filename*=utf-8''Download_14Jul20151332195868.xlsx

If I remove above lines from the file and save it again, then I am able to open the file in excel. Same thing happens with any other file type.

I am using below URL to POST the file to ServiceNow: https://mycompany.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=1abc60ccdabc1c14215fc082ba9619b0&file_name=SampleExcel3.xlsx

And below is my code:

        private static string SendMultiPartRequest(string URL, ExecutionEnvironment executionEnvironment)
        {
            var response = "";
            try
            {
                byte[] file_bytes = File.ReadAllBytes(AttachmentFilePath);
                if (!string.IsNullOrWhiteSpace(AttachmentFilePath))
                {
                    using (var client = CreateNewClient(URL, executionEnvironment))
                    {
                        using (var multipartContent = new MultipartFormDataContent())
                        {
                            multipartContent.Add(new StreamContent(new MemoryStream(file_bytes)), "Attachment", AttachmentFilePath.Substring(AttachmentFilePath.LastIndexOf("\\") + 1));
                            //multipartContent.Headers.Remove("Content-Type");

                            Task responseTask = client.PostAsync(WSIUrl, multipartContent).ContinueWith((Task<HttpResponseMessage> authRes) =>
                            {
                                response = HandleResponse(authRes);
                            });

                            responseTask.Wait();
                        }
                    }
                }
                else
                {
                    response = "{ \"ErrorMessage\" : \"Attachment file not specified.\"}";
                }
            }
            catch (Exception ex)
            {
                response = "{ \"ErrorMessage\" : \"Unspecified error: " + ex.Message + " \"}";
            }
            return response;
        }

I also tried to Remove the header but it failed to attach file when I un-comment this line:

  //multipartContent.Headers.Remove("Content-Type");

I don't have control over how ServiceNow API is using the file submitted. Please suggest how can I submit a binary file to attach to ServiceNow ticket.

UPDATE: I am still trying with various options but still no luck. I tried to explore how Postman is able to attach the file successfully and found below code from Postman application. However, I can't see in this code how Postman is adding the Binary contents in the payload:

var client = new RestClient("https://mycompany.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=1abc60ccdabc1c14215fc082ba9619b0&file_name=Sample.xlsx");
var request = new RestRequest(Method.POST);
request.AddHeader("Postman-Token", "34584fo4-f91a-414f-8fd0-ff44b0c6b345");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("Authorization", "Basic c4Ajc2NvcmNoOmVOdBEzOSNSQGspqr==");
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");
IRestResponse response = client.Execute(request);

However, when I send the POST request through Postman Application, it is working fine: URL used in postman is: POST - https://MyCompany.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=1abc60ccdabc1c14215fc082ba9619b0&file_name=Sample.xlsx

enter image description here


Solution

  • I finally got this working using RestSharp. Below is the code if anyone else is looking for the solution:

    private static string SendMultiPartRestClient(string URL, ExecutionEnvironment executionEnvironment)
    {
        string response;
        try
        {
            if (!string.IsNullOrWhiteSpace(AttachmentFilePath))
            {
                string FileNameWithoutExtension = Path.GetFileNameWithoutExtension(AttachmentFilePath);
                string FileExtension = Path.GetExtension(AttachmentFilePath);
                string AttachmentFileName = $"{FileNameWithoutExtension}{FileExtension}";
    
                string AskNowPasswordToBeUsed;
                if (executionEnvironment == ExecutionEnvironment.NonProduction)
                    AskNowPasswordToBeUsed = AskNowPasswordEagle;
                else
                    AskNowPasswordToBeUsed = AskNowPasswordProduction;
    
                byte[] byteArray = Encoding.ASCII.GetBytes(AskNowUserName + ":" + AskNowPasswordToBeUsed);
                var Auth = Convert.ToBase64String(byteArray);
    
                ServicePointManager.Expect100Continue = true;
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
                
                X509Certificate2 c1 = new X509Certificate2(asknowCertPath, CertPass);
    
                var client = new RestClient(WSIUrl);
                client.Timeout = -1;
                client.AddDefaultHeader(DP_EXTERNAL_URL, URL);
                client.ClientCertificates = new X509CertificateCollection() { c1 };
    
                var request = new RestRequest(Method.POST);
                request.AddHeader("Authorization", $"Basic {Auth}");
                request.AddHeader("Accept", "*/*");
                request.AddHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                request.AddParameter("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    File.ReadAllBytes(AttachmentFilePath),
                    ParameterType.RequestBody);
    
                IRestResponse restResponse = client.Execute(request);
    
                switch (restResponse.ResponseStatus)
                {
                    case ResponseStatus.None:
                        response = "{ \"ErrorMessage\" : \"No response\"}";
                        break;
                    case ResponseStatus.Completed:
                        response = restResponse.Content;
                        break;
                    case ResponseStatus.Error:
                        response = "{ \"ErrorMessage\" : \"Unspecified error: " + restResponse.ErrorMessage + " \"}";
                        break;
                    case ResponseStatus.TimedOut:
                        response = "{ \"ErrorMessage\" : \"Request timed out\"}";
                        break;
                    case ResponseStatus.Aborted:
                        response = "{ \"ErrorMessage\" : \"Request aborted\"}";
                        break;
                    default:
                        response = "{ \"ErrorMessage\" : \"Unspecified response type.\"}";
                        break;
                }
            }
            else
            {
                response = "{ \"ErrorMessage\" : \"Attachment file not specified.\"}";
            }
        }
        catch (Exception ex)
        {
            response = "{ \"ErrorMessage\" : \"Unspecified error: " + ex.Message + " \"}";
        }
        return response;
    }