1

I have checked other answers before posting this, so please try not to jump straight to conclusions here. I'm using HttpClient to connect to the API for an application we use, but it appears to not be recognising or finding one of the parameters it requires. The body just a custom object.

This initially didn't work in Postman, but when I changed the Content-Type and Body to 'x-www-form-urlencoded' I would receive a 200 response and the required information in return. Looking on here I hoped that switching from StringContent to FormUrlEncodedContent would help, but I still get the same end result.

Here is a cut down version of my code, I hope someone can help. Perhaps I've missed a step.

internal class API
{
    private static HttpResponseMessage Response;
    private static HttpClient Client;
    private static FormUrlEncodedContent UrlEncodedContent;
    private static Dictionary<string, string> bodyDictionary;

    internal static async Task<string> Invoke(string command, string 
name, object body = null, string method = "GET")
    {
        // Create HTTP Client
        Client = new HttpClient();
        Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        string url = "https://" + name + "/Application/" + command;

        switch(method)
        {
            case "POST":
                bodyDictionary = Convert.ConvertClassToDictionary(body); // no problem here. fully tested      
                UrlEncodedContent = new FormUrlEncodedContent(bodyDictionary);
                Post(url, UrlEncodedContent).Wait();
                UrlEncodedContent.Dispose();
                break;
        }

        return Response;
    }

    static private async Task Post(string url, FormUrlEncodedContent content)
    {
        Response = await Client.PostAsync(url, content);
    }
}

It works fine in powershell and is very simple, but it isn't an option here.

$uri = "https://application.com/application/token"
$headers = @{"Accept"="application/json"; "Content-Type"="application/x-www-form-urlencoded"}
$body = @{"username"="user";"password"="pass";"grant_type"="password";"client_id"="id12345"}
$response = Invoke-RestMethod -Method Post -Uri $uri -Body $body -Headers $headers
Ash
  • 3,030
  • 3
  • 15
  • 33

2 Answers2

4

You're going to need to create an HttpRequestMessage instance so that you can add the "Content-Type" header to it and use the HttpClient.SendAsync overload that accepts an HttpRequestMessage instead of a Uri.

Kyle Burns
  • 1,164
  • 7
  • 16
  • Hi Kyle. Thanks for your response. Do you need to add the Content-Type header here? I was under the impression that FormUrlEncodedContent did it for you? When I tried this earlier from the answer I linked, I could see the header, but still wasn’t getting the desired result with HttpRequestMessage. I will try again Monday morning. – Ash Sep 06 '19 at 20:00
1

looks this! the method GetResponse. I send an HttpRequestMessage (Request property) filled with the information that you are putting in the HttpClient.

public void SetContent<TEntity>(TEntity input)
{
    var json = JsonConvert.SerializeObject(input);

    if (Request.Method == HttpMethod.Post || Request.Method == HttpMethod.Put)
    {
        if (string.IsNullOrEmpty(ContentType) || ContentType == "application/json")
        {
            Request.Content = new StringContent(json, Encoding.UTF8, "application/json");
        }
        else
        {
            var parameters = JsonConvert.DeserializeObject<Dictionary<string, string>>(json)
                                        .Concat(CustomParameters)
                                        .Where(p => !string.IsNullOrEmpty(p.Value));

            Request.Content = new FormUrlEncodedContent(parameters);
        }
    }
}

public Out GetResponse<TEntity, Out>(HttpClient client, TEntity input)
{
    var response = client.SendAsync(Request).Result;
    var content = response.Content.ReadAsStringAsync().Result;

    SetHttpClientDataToEntity(input, response);

    if (!response.IsSuccessStatusCode)
    {
        ErrorDetail error;

        throw content.TryParseJson(out error)
                    ? new Exception(error.Message)
                    : throw new Exception(content);
    }

    Out result;
    content.TryParseJson<Out>(out result);
    return result;
}
agua from mars
  • 16,428
  • 4
  • 61
  • 70
Vicente
  • 166
  • 1
  • 9
  • Hi Vicente. Thanks for you response. I’m not sure I fully understand your answer, I’m still a little new to this, but I’ll take a look on Monday morning. – Ash Sep 06 '19 at 20:05
  • OK! I give you an advice, don't use static classes in scenaries like these. because your class will share httpclient's instance with whole application, and you could get "networks" errors. – Vicente Sep 06 '19 at 20:30
  • Vicente, thank you. The example really helped me out here and I now receive my response. Worth noting that Kyle's answer is also correct, but I needed to use the DeserializeObject method too for it to finally work for me. Obviously I had been sending an incorrect key name. – Ash Sep 09 '19 at 08:37