Search code examples
c#httpwebrequesthttp-posthttp-status-code-400httpcontent

HTTP Post Request returns 400 C#


I am trying to make a http post request to obtain an api token. If successful, it is supposed to return string values of access token, token type (bearer) and expires_in.

The code I have is a generic one which I was expecting to see working. But for some reasons, it throws an exception of 400 "The remote server returned an error. Bad Request". I have been trying everything to fix this whereas the result doesn't change.

When I debug the code and see the result in the Output window, there is an exception about Data stream saying "this stream doesn't support seek operations"

Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();

My doubt is it happens due to the postData, the way it is encoded. My client secret is something like:

g/gOvqf5R+FTZZXbwsCbp0WsQjF9B0bl87IBQ8VAJ2Q=

Does it encode the characters in the secret itself so that it constructs a bad request?

I have also tried this on POSTMAN and it produced a result, so there is nothing with the api. It comes down again to the request content. It's a console app. I am pasting my code below and I am thankful for your help in advance.

public static APIModel GenerateApiKey()
    {
        var appSettings = ConfigurationManager.AppSettings;

        try
        {
            var urlToCall = string.Format("https://app.example.com/token");
            var uri = new Uri(urlToCall);

            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.Method = "POST";

            string postData = "grant_type=client_credentials&client_id=" + appSettings["my_client_id"] + "&client_secret=" + appSettings["my_client_secret"];
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = byteArray.Length;


            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            var response = (HttpWebResponse)request.GetResponse();

            APIModel bearerToken;

            using (StreamReader sr = new StreamReader(response.GetResponseStream()))
            {
                string jsonFromServer = sr.ReadToEnd();
                bearerToken = JsonConvert.DeserializeObject<APIModel>(jsonFromServer); 
            }

            response.Close();

            return bearerToken;

        }
        catch (Exception e)
        {
            throw new Exception("Error getting a response from API  " + e.Message);
        }

    }

Solution

  • The remote server is giving you a 400 error due to you sending it incorrect data of some sort. You may be able to get the response and figure out the exact error - the remote server would quite possibly give you some more information. However, I can see one issue with your post data - the client secret needs to be URL encoded. Look at the content of it and you will see that it ends with an = sign. that will be interpreted as a special character. I also like to be a bit more explicit about creating strings, so this would work for you:

    var postItems = new List<KeyValuePair<string, string>>
    {
        new KeyValuePair<string, string>("grant_type", "client_credentials"),
        new KeyValuePair<string, string>("client_id", "client_credentials"),
        new KeyValuePair<string, string>("client_secret", "client_credentials"),
    };
    
    string postData = string.Join("&", 
        postItems.Select (kvp => 
            string.Format("{0}={1}", kvp.Key, HttpUtility.UrlEncode(kvp.Value))));