81

I am trying to fulfill this rest api:

public async Task<bool> AddTimetracking(Issue issue, int spentTime)
{
    // POST /rest/issue/{issue}/timetracking/workitem
    var workItem = new WorkItem(spentTime, DateTime.Now);
    var httpContent = new StringContent(workItem.XDocument.ToString());
    var requestUri = string.Format("{0}{1}issue/{2}/timetracking/workitem", url, YoutrackRestUrl, issue.Id);
    var respone = await httpClient.PostAsync(requestUri, httpContent);
    if (!respone.IsSuccessStatusCode)
    {
        throw new InvalidUriException(string.Format("Invalid uri: {0}", requestUri));
    }

    return respone.IsSuccessStatusCode;
}

workItem.XDocument contains the following elements:

<workItem>
  <date>1408566000</date>
  <duration>40</duration>
  <desciption>test</desciption>
</workItem>

I am getting an error from the API: Unsupported Media Type

I really have no idea how to resolve this, help is greatly appreciated. How do I marshall an XML file via a HTTP POST URI, using HttpClient?

5
  • You calculate the date as seconds but the example in your link uses msec. Commented Aug 17, 2014 at 19:04
  • @L.B, changed it to miliseconds, no luck. I guess I am doing the wrong thing with the HttpContent. Unfortunately google doesn't help me much yet Commented Aug 17, 2014 at 19:08
  • P.S: when I set up firefox poster with the exact same parameters, it works. Not that much luck trying from C#. Commented Aug 17, 2014 at 19:10
  • 1
    bas, download fiddler and see the difference between firefox and your code.. Commented Aug 17, 2014 at 19:12
  • 1
    @L.B, thx for the tip, I'll give that a go tomorrow. Commented Aug 17, 2014 at 19:14

4 Answers 4

109

You might want to set the mediaType in StringContent like below:

var httpContent = new StringContent(workItem.XDocument.ToString(), Encoding.UTF8, "text/xml");

OR

var httpContent = new StringContent(workItem.XDocument.ToString(), Encoding.UTF8, "application/xml");
Sign up to request clarification or add additional context in comments.

2 Comments

text/xml in my case
I know this answer is several years old but media type needed to be "text/xml" in my case as well.
28

You could use

var respone = await httpClient.PostAsXmlAsync(requestUri, workItem);

https://msdn.microsoft.com/en-us/library/system.net.http.httpclientextensions_methods

2 Comments

Looks like a better solution than the accepted answer
This method is not there in asp.net core 3.1
8

To use Matt Frear's solution you may need to add NuGet Package: Microsoft.AspNet.WebApi.Client

This brings in the extension methods so you can use:

var respone = await httpClient.PostAsXmlAsync<WorkItem>(requestUri, workItem);

This works for the newer .Net 5 so assume will work for asp.net core 3.1.

1 Comment

Maybe this works but I don't like that this package installs older versions of System.Memory and System.Threading.Tasks.Extensions. Also it installs Newtonsoft.
0

The accepted answer doesn't include the XML declaration, which may or may not matter. Below is a simple extension method for XDocument that prepends the XML declaration. Otherwise, the technique is identical:

// Extension method
public static string ToStringWithDeclaration(this XDocument doc, string declaration = null)
{
    declaration ??= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
    return declaration + doc.ToString();
}

// Usage (XDocument -> string -> UTF-8 bytes)
var content = new StringContent(doc.ToStringWithDeclaration(), Encoding.UTF8, "text/xml");
var response = await httpClient.PostAsync("/someurl", content);

If you want, you can also skip the step of converting the XDocument to a string (which is UTF-16 encoded), and just go right to UTF-8 bytes:

// Extension method
public static ByteArrayContent ToByteArrayContent(
    this XDocument doc, XmlWriterSettings xmlWriterSettings = null)
{
    xmlWriterSettings ??= new XmlWriterSettings();
    using (var stream = new MemoryStream())
    {
        using (var writer = XmlWriter.Create(stream, xmlWriterSettings))
        {
            doc.Save(writer);
        }
        var content = new ByteArrayContent(stream.GetBuffer(), 0, (int)stream.Length);
        content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
        return content;
    }
}

// Usage (XDocument -> UTF-8 bytes)
var content = doc.ToByteArrayContent();
var response = await httpClient.PostAsync("/someurl", content);

// To view the serialized XML as a string (for debugging), you'd have to do something like:
using var reader = new StreamReader(content.ReadAsStream());
string xmlStr = reader.ReadToEnd();

For a third option, XDocument.Save/XmlWriter can also output to a string via StringBuilder (as shown in the documentation). That is useful if you want a string but need control over formatting (such as whether to use indentation). Just be aware that the XML declaration generated via this method will always say "utf-16" (regardless of XmlWriterSettings.Encoding), so you probably want to specify XmlWriterSettings.OmitXmlDeclaration = true and add the declaration manually afterward. See this question for some discussion of that issue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.