I am developing an app that integrates with Freshdesk to create tickets, so I am using the Freshdesk API to make requests.
- The app is built in C# (.NET Core 3.1) using the
HttpClient
library. - I am creating tickets with attachments and built the code following the structure described on your sample app
Unlike your sample code which is manually constructed, the HttpClient
library constructs the request automatically and we have little control over this.
I have had some issues making requests using the HttpClient library (which I think most people will). Essentially the request structure requirements are too strict.
Please refer to the sample code below for more info.
- The ‘Content-Disposition’ header too strict
- Array names formatted like
name="tags[0]"
doesn’t work butname="tags[]"
does (see ‘TODO: (1)’) - The ‘name’ field always needs to be quoted (not part of the RFC) (see ‘TODO: (2)’)
- Array names formatted like
- Request is rejected if ‘Content-Type’ header is added to each form part (see ‘TODO: (3)’)
private HttpContent GetHttpContent(CreateTicketPayload payload)
{
HttpContent content;
var dispositionType = "form-data";
content = new MultipartFormDataContent();
var multiContent = content as MultipartFormDataContent;
foreach (var attachment in payload.Attachments)
{
multiContent.Add(new ByteArrayContent(attachment.Data)
{
Headers =
{
ContentType = MediaTypeHeaderValue.Parse(attachment.ContentType),
ContentDisposition = new ContentDispositionHeaderValue(dispositionType)
{
Name = "attachments[]",
FileName = attachment.FileName,
CreationDate = attachment.CreationDate,
}
}
});
}
// TODO: (1) Breaks Freshdesk - Freshdesk Content-Disposition header too strict:
// BROKEN: Content-Disposition: form-data; name="tags[0]"
// WORKS: Content-Disposition: form-data; name="tags[]"
// NOTE: "payload.ToDictionary()" returns a Dictionary<string,string>() representing the payload data -- e.g. { "subject": "sub", "tags[0]": "tag1", "tags[1]": "tag2" }
foreach (var item in payload.ToDictionary())
{
multiContent.Add(new ByteArrayContent(System.Text.Encoding.ASCII.GetBytes(item.Value))
{
Headers =
{
ContentDisposition = new ContentDispositionHeaderValue(dispositionType)
{
// TODO: (2) Breaks Freshdesk - because Freshdesk needs all values to be quoted. Manually using 'Parameters' instead
//Name = item.Key,
Parameters =
{
new NameValueHeaderValue("name", $"\"{item.Key}\"")
}
},
// TODO: (3) Breaks Freshdesk
// ContentType = new MediaTypeHeaderValue("text/plain")
// {
// CharSet = "utf-8"
// }
}
});
}
return content;
}
Sample (broken) request ‘content’:
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Type: message/rfc822
Content-Disposition: form-data; name="attachments[]"; filename="39f7f39a-bd17-4ab8-3eb5-cc46b5868195.eml"; creation-date="Wed, 30 Sep 2020 23:51:46 GMT"
MIME-Version: 1.0
Date: Thu, 1 Oct 2020 00:51:40 +0100
Message-ID: <12345+abFCBUE@mail.gmail.com>
Subject: m-freshdesk
From: example <example@gmail.com>
To: example <example@gmail.com>
Content-Type: multipart/alternative; boundary="0000000000001af1d105b09095b1"
--0000000000001af1d105b09095b1
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: base64
MjAg3J+Xjw0L
--0000000000001af1d105b09095b1
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: base64
PGRpdiBkaXI911xciI+MjDCiaCfmY88L9Rpdw4NCg==
--0000000000001af1d105b09095b1--
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=status
2
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=priority
1
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=email
example@example.com
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=subject
Ticket subject
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=description
<p>Ticket description here</p>
<p>Freshdesk test</p>
<p>Should include attachments</p>
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=type
Type
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name="tags[0]"
RandomTag
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name="tags[1]"
AnotherTag
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=fr_due_by
2020-10-01T00:46:50.9872764Z
--71b0658e-2982-47a9-964e-ef177b87ca2c
Content-Disposition: form-data; name=due_by
2020-10-01T03:21:50.9892658Z
--71b0658e-2982-47a9-964e-ef177b87ca2c--