1

The following is the code from where I would return a tuple of response status code and response output.

private Tuple<int, string> API_Check(string URL, string reqtype, string reqbody, string split_username, string split_pwd)
    {
        string responsetxt="";
        HttpResponseMessage httpresult = new HttpResponseMessage();
        int statuscode = 0;
        ServicePointManager.Expect100Continue = true;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
        ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
        HttpClient _httpClient = new HttpClient();
        var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username+":" + split_pwd));
        _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
        try
        {
            using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))
            {
                if (reqtype == "GET")
                {
                    httpresult = _httpClient.GetAsync(URL).Result;
                }
                if (reqtype == "PUT")
                {
                    httpresult = _httpClient.PutAsync(URL, content).Result;
                    //httpresult = _httpClient.PutAsync()
                }
                if (reqtype == "POST")
                {
                    httpresult = _httpClient.PostAsync(URL, content).Result;
                }

                statuscode = (int)httpresult.StatusCode;
                responsetxt = httpresult.Content.ReadAsStringAsync().Result;
                return Tuple.Create(statuscode, responsetxt);
            }
        }
        catch (System.Net.WebException Excptn)
        {
            statuscode = 401;
            responsetxt = Excptn.Status.ToString();
            using (var stream = Excptn.Response.GetResponseStream())
            using (var reader = new StreamReader(stream))
            {
                MessageBox.Show(reader.ReadToEnd());
            }
        }
        return Tuple.Create(statuscode, responsetxt);
    }

For some reason, the request body is not getting filled correctly during the call. I'm getting 401 Unauthorized as for this Post call, which is definitely not a authorization error as the response message that I receive is equivalent to empty body or invalid input json format.

When I tried to hit the same reqbody for the endpoint with Postman, I'm getting 200 with valid response. Also, the GetAsync works for a similar API which doesn't require a body.

I verified there is no issues with the username, password or the Endpoint URL.

Is there a way, I could avoid using httpcontent and use the string as it is for hitting the API through C#?

Now, I could not use HttpWebRequest due to my current .Net framework limitations.

ItsMeGokul
  • 403
  • 1
  • 5
  • 16
  • 2
    `ReadAsStringAsync().Result;` <-- Why aren't you using `await`? – Dai Sep 22 '22 at 20:14
  • Are you maybe missing some of the required extra headers? – JHBonarius Sep 22 '22 at 20:22
  • @JHBonarius, Not much use. I replaced content with this and still getting the same error...... using (var content = new StringContent(JsonConvert.SerializeObject(reqbody), null, "application/json")) – ItsMeGokul Sep 22 '22 at 20:33
  • Your status code is overwritten when it gets to `catch` block. Try to comment out this block and see the actual response status code. – Serhii Sep 22 '22 at 20:37
  • Did you catch any exceptions? – Serhii Sep 22 '22 at 20:39
  • @Serhii, There are no exceptions as the status of the response is 401 – ItsMeGokul Sep 22 '22 at 20:56
  • @ItsMeGokul first line of `catch` block has ` statuscode = 401;`. Isn't it? – Serhii Sep 22 '22 at 21:42
  • @Serhii, the 401 was not from the catch. The response itself was 401 from ,,,,statuscode = (int)httpresult.StatusCode – ItsMeGokul Sep 22 '22 at 21:54
  • If `reqbody` is a string then why are you serializing it? Is it already JSON? Also `ServicePointManager.SecurityProtocol = ...` just NO. Do not set it explicitly, allow the operating system to choose the best protocol available. Shouldn't the `catch` be `catch ( HttpRequestException ex) { statuscode = ex.StatusCode ?? 0;` – Charlieface Sep 22 '22 at 23:50

1 Answers1

0

There are many issues with your code:

  • Primarily, you are serializing reqbody which is already a string. It sounds like you have a JSON string already, in which case you don't need to serialize it.
  • Don't use .Result, it can cause a deadlock. use await instead.
  • Use Valuetuples instead of Tuple, which can be inefficient.
  • Do not set ServicePointManager.SecurityProtocol = ..., instead let the operating system choose the best security protocol.
  • Do not use ServicePointManager in general, as it affects all HTTP request from your app. Instead set the relevant HtppClient property, or better: use HttpRequestMessage and set it directly on the message.
  • You can simplify the code a bit if you use HttpRequestMessage, giving it the type of HTTP method
  • You are catching the wrong exception type. You should be catching HttpRequestException, from which you can get the actual StatusCode.
  • HttpClient by default does not throw on non-success codes. You need to handle them explicitly.
  • Cache the HttpClient, or you could get socket exhaustion.
  • Creating a new HttpResponseMessage doesn't make a huge amount of sense.
HttpClient _httpClient = new HttpClient {
    DefaultRequestHeaders = {
        ExpectContinue = false,
    },
};

private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, string reqbody, string split_username, string split_pwd)
{
    var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username + ":" + split_pwd));
    try
    {
        using (var content = new StringContent(reqbody))
        using (var request = new HttpRequestMessage(URL, reqtype))
        {
            message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
            if (reqtype != "GET")
                message.Content = content;

            using var httpresult = await _httpClient.SendAsync(URL, content);
            var statuscode = (int)httpresult.StatusCode;
            var responsetxt = await httpresult.Content.ReadAsStringAsync();
            if (!httpresult.IsSuccessStatusCode)
                MessageBox.Show(responsetxt);

            return (statuscode, responsetxt);
        }
    }
    catch (HttpRequestException ex)
    {
        var statuscode = ex.StatusCode ?? 0;
        var responsetxt = ex.Message;
        MessageBox.Show(responsetxt);
        return (statuscode, responsetxt);
    }
}

If you actually have an object to serialize then change the method to

private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, object reqbody, string split_username, string split_pwd)
{
....
....
        using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))
Charlieface
  • 52,284
  • 6
  • 19
  • 43