Jump to content

Send post data with an array using IHttpClient


rechigo

Recommended Posts

rechigo
After an hour of trying to figure out why sending my HTTP request would error with BadRequest, I figured it out, however I don't know how to fix it.

Basically, to set post data you need to a dictionary of strings, which is the problem here. For the embed key, the value(array of embeds) keeps getting wrapped in quotes because it's a dictionary of strings, makes sense.

 

The problem is the Discord API isn't expecting the array wrapped in quotes:



{"embeds":"[{\"color\":1222617,\"title\":\"It works!\",\"description\":\"This is a test notification from Emby\"}]","username":"MBCord","avatar_url":"https://i.imgur.com/4k4fUmy.png"}


 

it's expecting the embed array like this (not in quotes):



{"embeds":[{\"color\":1222617,\"title\":\"It works!\",\"description\":\"This is a test notification from Emby\"}],"username":"MBCord","avatar_url":"https://i.imgur.com/4k4fUmy.png"}


 

But since I'm putting my array in a dictionary of strings, it wraps my array in quotes causing the Discord API to return a 400

 

How can I do this without my array getting wrapped in quotes before it's sent?

 

 

Relevant code:

 



var options = GetOptions(request.UserID);

List<DiscordEmbed> Embeds = new List<DiscordEmbed> { };
Embeds.Add(new DiscordEmbed { color = int.Parse(options.EmbedColor.Substring(1, 6), System.Globalization.NumberStyles.HexNumber), title = "It works!", description = "This is a test notification from Emby" });


var parameters = new Dictionary<string, string> { };

parameters.Add("embeds", _jsonSerializer.SerializeToString(Embeds));
parameters.Add("username", options.Username);
parameters.Add("avatar_url", options.AvatarUrl);

_logger.Debug("Discord Request to: {0} From: {1} With: {2}", options.DiscordWebhookURI, _userManager.GetUserById(request.UserID).Name, _jsonSerializer.SerializeToString(parameters));

var httpRequestOptions = new HttpRequestOptions { };

httpRequestOptions.Url = options.DiscordWebhookURI;
httpRequestOptions.RequestHeaders["Content-Type"] = "application/json";
httpRequestOptions.SetPostData(parameters);

using (await _httpClient.Post(httpRequestOptions).ConfigureAwait(false))
{

}


 

I'm new to C# so please bear with me

Edited by rechigo
Link to comment
Share on other sites

Two things come to mind here.

 

First you could try

 

Dictionary<string, object>

 

Or

 

Dictionary<string, DiscordEmbed>

 

Without seeing a little more it's only a guess.

 

I haven't had a great deal of success serializing dictionaries into json data.

I have had to use List<List<T>> in the past.

 

Would you want to share your work with me then I could be a bit more helpful. Is there some API docs to scan over to better understand the object types in the json data? :)

Link to comment
Share on other sites

rechigo

Two things come to mind here.

 

First you could try

 

Dictionary<string, object>

 

Or

 

Dictionary<string, DiscordEmbed>

 

Without seeing a little more it's only a guess.

 

I haven't had a great deal of success serializing dictionaries into json data.

I have had to use List<List<T>> in the past.

 

Would you want to share your work with me then I could be a bit more helpful. Is there some API docs to scan over to better understand the object types in the json data? :)

I created a DiscordMessage class which also implements DiscordEmbed, however I can't do something like Dictionary<string, DiscordMessage> as SetPostData expects a Dictionary<string, string>

 

Here is the official documentation on executing webhooks: https://discordapp.com/developers/docs/resources/webhook#execute-webhook

 

If you want to see the code, I can send you the github repo when I get home and push to it

 

Sent from my SM-G973U using Tapatalk

Link to comment
Share on other sites

Try this:

        public class Embed
        {
            public int color { get; set; }
            public string title { get; set; }
            public string description { get; set; }
        }

        public class PostData
        {
            public List<Embed> embeds { get; set; }
            public string username { get; set; }
            public string avatar_url { get; set; }
        }

        public string getPostDataJson(MY_VARS_HERE)
        {
            return JsonSerializer.SerializeToString(new PostData()
            {
                avatar_url = "MY_URL",
                username = "MY_USER_NAME",
                embeds = new List<Embed>()
                {
                    new Embed()
                    {
                        color = 18181818, //My_COLOR_INT
                        description = "MY_DESCRIPTION",
                        title = "MY_TITLE"
                    }
                    //You could add another embeded item here inside the list.
                }
            });
            
        }

Use:

var postData = getPostDataJson(My_VARS_HERE)

Try to send that as postData.

 

The thing is, C# Dictionaries aren't really a thing (... I mean it's possible but they are not the same... really.... ) in JSON.

 

You have to create classes and have Lists<T> inside them.

 

In order to get a jump start on turning JSON data requirements into C# Classes check out this site, it will change your coding life! :)

 

http://json2csharp.com/

 

Let me know how it goes!

Edited by chef
Link to comment
Share on other sites

rechigo

Try this:

        public class Embed
        {
            public int color { get; set; }
            public string title { get; set; }
            public string description { get; set; }
        }

        public class PostData
        {
            public List<Embed> embeds { get; set; }
            public string username { get; set; }
            public string avatar_url { get; set; }
        }

        public string getPostDataJson(MY_VARS_HERE)
        {
            return JsonSerializer.SerializeToString(new PostData()
            {
                avatar_url = "MY_URL",
                username = "MY_USER_NAME",
                embeds = new List<Embed>()
                {
                    new Embed()
                    {
                        color = 18181818, //My_COLOR_INT
                        description = "MY_DESCRIPTION",
                        title = "MY_TITLE"
                    }
                    //You could add another embeded item here inside the list.
                }
            });
            
        }

Use:

var postData = getPostDataJson(My_VARS_HERE)

Try to send that as postData.

 

The thing is, C# Dictionaries aren't really a thing (... I mean it's possible but they are not the same... really.... ) in JSON.

 

You have to create classes and have Lists<T> inside them.

 

In order to get a jump start on turning JSON data requirements into C# Classes check out this site, it will change your coding life! :)

 

http://json2csharp.com/

 

Let me know how it goes!

This makes sense, but how am I supposed to set postData as my post data when the SetPostData method only accepts Dictionary<string,string>?

Link to comment
Share on other sites

            try
            {
                await _httpClient.Post(
                        new HttpRequestOptions
                        {
                            Url = "",
                            CancellationToken = CancellationToken.None,
                            EnableHttpCompression = false,
                            RequestContent = JSON_CONTENT_HERE_MAYBE
                        })
                    .ConfigureAwait(false);
                
            } catch { }
        }

Please try this. But, I'm not entirely sure. 

 

If this doesn't work, let me know, and I'll keep reading Github to find an example as a sure thing. 

 

What happens if you place the serialized jsonData inside SetPostData()

httpRequestOptions.SetPostData(SERIALIZED_JSON_HERE);
Edited by chef
Link to comment
Share on other sites

rechigo

What happens using SetPostData with postData as the data

 

 w65.png

 

what happens when using RequestContent

u6i.png

Link to comment
Share on other sites

rechigo

OKAY, I finally got it working, but there's one catch: I'm have to use the System.Net HttpClient, not Emby's IHttpClient...

var postData = new StringContent(_jsonSerializer.SerializeToString(new DiscordMessage()
            {
                avatar_url = options.AvatarUrl,
                username = options.Username,
                embeds = new List<DiscordEmbed>()
                {
                    new DiscordEmbed()
                    {
                        color = 181818,
                        description = "That's RIGHT",
                        title = "HA"
                    }
                }
            }).ToString());

            try
            {
                var RequestMessage = new HttpRequestMessage(HttpMethod.Post, options.DiscordWebhookURI);
                RequestMessage.Content = postData;
                RequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                await _httpClient.SendAsync(RequestMessage).ConfigureAwait(false);
            } catch (HttpRequestException e)
            {
                _logger.Error("Failed to make request to Discord: {0}", e);
            }
Edited by rechigo
  • Like 1
Link to comment
Share on other sites

 

OKAY, I finally got it working, but there's one catch: I'm have to use the System.Net HttpClient, not Emby's IHttpClient...

var postData = new StringContent(_jsonSerializer.SerializeToString(new DiscordMessage()
            {
                avatar_url = options.AvatarUrl,
                username = options.Username,
                embeds = new List<DiscordEmbed>()
                {
                    new DiscordEmbed()
                    {
                        color = 181818,
                        description = "That's RIGHT",
                        title = "HA"
                    }
                }
            }).ToString());

            try
            {
                var RequestMessage = new HttpRequestMessage(HttpMethod.Post, options.DiscordWebhookURI);
                RequestMessage.Content = postData;
                RequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                await _httpClient.SendAsync(RequestMessage).ConfigureAwait(false);
            } catch (HttpRequestException e)
            {
                _logger.Error("Failed to make request to Discord: {0}", e);
            }

 

Well done!

  • Like 1
Link to comment
Share on other sites

As a side note, in your project you have to add a nuget package to be able to use 'AsMemory'.

 

System.Memory

Edited by chef
Link to comment
Share on other sites

  • 1 month later...
mickle026

I need to learn c# ..... ;)  This might or might not be relevant, but just so you know...

 

In vb.net if you do a string array of strings , ie dictionary <string,string>  you get the data wrapped in quotes.  What you do is create an array of strings, not a string array of strings - if that makes sense.

 

Your dictionary array looks like a standard 2d array in vb.net, but its not.  However you can use them the same

 

so in vb. it would be,  Dim MyArray(,)={{"1st element key","1st element data"},{"2nd element key","2nd element data"}, .... and so on }

 

I think in c# it would be like this .... string[,] array = new string[,] { {"1st element key", "1st element data"}, {"2nd element ke", "2nd element data"}, };

 

This array shouldn't wrap the string in quotes..

Edited by mickle026
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...