How do I send arbitrary JSON data, with a custom header, to a REST server?

12,943

Solution 1

/// <summary>
    /// Creates a new instance of the <see cref="T:System.Net.Http.StringContent"/> class.
    /// </summary>
    /// <param name="content">The content used to initialize the <see cref="T:System.Net.Http.StringContent"/>.</param><param name="encoding">The encoding to use for the content.</param><param name="mediaType">The media type to use for the content.</param>
    [__DynamicallyInvokable]
    public StringContent(string content, Encoding encoding, string mediaType)
      : base(StringContent.GetContentByteArray(content, encoding))
    {
      this.Headers.ContentType = new MediaTypeHeaderValue(mediaType == null ? "text/plain" : mediaType)
      {
        CharSet = encoding == null ? HttpContent.DefaultStringEncoding.WebName : encoding.WebName
      };
    }

It's constructor of StringContent. Looks like that you should specify appropriate Encoding and mediaType

Solution 2

You can't directly setup an instance of HttpContent, because it is an abstract class. You need to use one of the sub-classes, depending on your need. Most likely StringContent, which lets you set the string value of the response, the encoding, and the media type in the constructor: http://msdn.microsoft.com/en-us/library/system.net.http.stringcontent.aspx

Answer from How do I set up HttpContent for my HttpClient PostAsync second parameter?

Share:
12,943
makerofthings7
Author by

makerofthings7

Updated on July 31, 2022

Comments

  • makerofthings7
    makerofthings7 almost 2 years

    TL;DR -- How do I send a JSON string to a REST host with an auth header? I've tried 3 different approaches found one that works with anonymous types. Why can't I use anonymous types? I need to set a variable called "Group-Name", and a hyphen isn't a valid C# identifier.

    Background

    I need to POST JSON but am unable to get the body and the content type correct

    Function #1 - Works with anonymous types

    The content type and data is correct, but I don't want to use anonymous types. I want to use a string

      static void PostData(string restURLBase, string RESTUrl, string AuthToken, string postBody)
        {
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri(restURLBase);
            client.DefaultRequestHeaders.Add("Auth-Token", AuthToken);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
            // StringContent content = new StringContent(postBody);
    
            var test1 = "data1";
            var test2 = "data2";
            var test3 = "data3";
    
            var response = client.PostAsJsonAsync(RESTUrl, new { test1, test2, test3}).Result;  // Blocking call!
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
                return;
            } 
        }
    

    Output #1

    Content type and data is correct when using AnonymousTypes + PostAsJsonAsync, but I don't want to use anonymous types.

    POST https://api.dynect.net/REST/Zone/ABCqqqqqqqqqqqqYYYYYtes3ss.com HTTP/1.1
    Auth-Token: --- REDACTED -----
    Accept: application/json
    Content-Type: application/json; charset=utf-8
    Host: api.dynect.net
    Content-Length: 49
    Expect: 100-continue
    
    {"test1":"data1","test2":"data2","test3":"data3"}
    

    Function #2 - Doesn't work as expected

    Take a string and put it into a StringContent object. This has a side effect of changing the content type.

      static void PostData(string restURLBase, string RESTUrl, string AuthToken, string postBody)
        {
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri(restURLBase);
            client.DefaultRequestHeaders.Add("Auth-Token", AuthToken);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
            StringContent content = new StringContent(postBody);
    
            var response = client.PostAsync(RESTUrl, content).Result;  // Blocking call!
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
                return;
            } 
        }
    

    Output #2

    Content type is wrong when using StringContent + PostAsync

    POST https://api.dynect.net/REST/Zone/ABCqqqqqqqqqqqqYYYYYtes3ss.com HTTP/1.1
    Auth-Token: ---- REDACTED -------
    Accept: application/json                      // CORRECT
    Content-Type: text/plain; charset=utf-8       // WRONG!!!
    Host: api.dynect.net
    Content-Length: 100
    Expect: 100-continue
    
    {"rdata" : ["rname" : "dynect.nfp.com", "zone" : "ABCqqqqqqqqqqqqYYYYYtes3ss.com"], "ttl" : "43200"}
            // ^^ THIS IS CORRECT
    

    Function #3 - Doesn't work as expected

    Since I know PostAsJsonAsync sets the contentType correctly, lets use that method. (doesn't work)

        static void PostData(string restURLBase, string RESTUrl, string AuthToken, string postBody)
        {
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri(restURLBase);
            client.DefaultRequestHeaders.Add("Auth-Token", AuthToken);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
            StringContent content = new StringContent(postBody);
    
            var response = client.PostAsJsonAsync(RESTUrl, content).Result;  // Blocking call!
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
                return;
            } 
        }
    

    Output #3

    Content type is correct, POST body is wrong when using StringContent + PostAsJsonAsync

    POST https://api.dynect.net/REST/Zone/ABCqqqqqqqqqqqqYYYYYtes3ss.com HTTP/1.1
    Auth-Token: -- REDACTED ---
    Accept: application/json
    Content-Type: application/json; charset=utf-8
    Host: api.dynect.net
    Content-Length: 74
    Expect: 100-continue
    
    {"Headers":[{"Key":"Content-Type","Value":["text/plain; charset=utf-8"]}]}
    

    Question

    All I want to do is send JSON as a string, or dynamic object defined at runtime, to a server, with HTTP content type correct, and with a special 'Auth-Token' header.

    Any example, if not using WebAPI, such as servicestack, or anything else is cool.