Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 398
makerofthings7 Avatar asked Jan 11 '14 01:01

makerofthings7


People also ask

How do I send a custom request header?

For example, to send a GET request with a custom header name, you can use the "X-Real-IP" header, which defines the client's IP address. For a load balancer service, "client" is the last remote host. Your load balancer intercepts traffic between the client and your server.

What HTTP header is used to indicate that JSON is being used as the payload?

A PUT request header.

Can you send JSON data in a GET request?

To answer your question, yes you may pass JSON in the URI as part of a GET request (provided you URL-encode).


1 Answers

/// <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

like image 170
Daniil Grankin Avatar answered Nov 14 '22 22:11

Daniil Grankin