Search code examples
c#httprequesthttpclient

HTTPClient results in BadRequest when attempting to pass through parameters


I am very new with making calls to API, and am trying to contact the following api: https://www.watttime.org/api-documentation/

Basically, I am attempting to convert the two snippets of python code to an HttpRequest within c#:

For logging in:

import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
rsp = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog'))
print(rsp.json())

For grabbing data with token:

import requests
from requests.auth import HTTPBasicAuth

login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth(‘freddo’, ‘the_frog’)).json()['token']

region_url = 'https://api2.watttime.org/v2/ba-from-loc'
headers = {'Authorization': 'Bearer {}'.format(token)}
params = {'latitude': '42.372', 'longitude': '-72.519'}
rsp=requests.get(region_url, headers=headers, params=params)
print(rsp.text)

Here is my c# code attempting to emulate that behavior:

 private async void TestLogin()
        {
            var token = "";
            HttpClientHandler d = new HttpClientHandler();
            d.Credentials = new NetworkCredential("myUsername", "myPassword");
            using (var client = new HttpClient(d))
            {

                client.BaseAddress = new Uri("https://api2.watttime.org");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                //log in and token grab
                HttpResponseMessage response = await client.GetAsync("v2/login");
                if (response.IsSuccessStatusCode)
                {
                    string apiResponse = await response.Content.ReadAsStringAsync();

                    var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(apiResponse);
                    tokenValue = values["token"];

                    
                }
                else
                {
                    Console.WriteLine("Internal server Error");
                }
            }

            
            using (var client2 = new HttpClient())
            {

                client2.BaseAddress = new Uri("https://api2.watttime.org");
                client2.DefaultRequestHeaders.Accept.Clear();

                client2.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client2.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenValue.ToString());

                var url = "v2/ba-from-loc";

                var query = HttpUtility.ParseQueryString(string.Empty);
                query["latitude"] = "43.80136";
                query["longitude"] = "-91.23958";
                string queryString = query.ToString();


                
                HttpResponseMessage response2 = await client2.GetAsync(url);
                if (response2.IsSuccessStatusCode)
                {

                }
                else
                {
                    Console.WriteLine("Internal server Error Okay?");
                }

                //GET Method

            }
        }

When I use the code above, the part part that grabs and stores the token with my username and password entered works correctly and I am able to get that token back. However, during the second HttpRequest, I get an Error 400: Bad Request.

I then changed the following code within the second HttpRequest:

HttpResponseMessage response2 = await client2.GetAsync(url);

To:

HttpResponseMessage response2 = await client2.GetAsync(url + queryString);

In doing this, I thought I was adding the needed parameters to make it a "good request" instead of a "bad" one. Instead, I then received an Error 404: Not Found, which I believe means I am getting further away from the solution. When it boils down, I am wondering how to correctly add these parameters so the API call works.


Solution

  • Simply running your query string builder on Linqpad shows me that

    var query = HttpUtility.ParseQueryString(string.Empty);
    query["latitude"] = "43.80136";
    query["longitude"] = "-91.23958";
    string queryString = query.ToString();
    
    queryString.Dump();
    

    queryString is

    latitude=43.80136&longitude=-91.23958
    

    This means url + querystring is

    v2/ba-from-loclatitude=43.80136&longitude=-91.23958
    

    I suspect you want to call client2.GetAsync with

    v2/ba-from-loc?latitude=43.80136&longitude=-91.23958
    

    In which case you want to call

    client2.GetAsync(url + "?" + queryString);