0

I am new to writing generic methods in C# and I am trying to create a generic SendAsync method in my c# project. Code is given below.

HttpClientHelper.cs

public static async Task<T2t> ExecuteRequest<T1,T2>(T2 request, string url)
{
    Uri requestUri = new Uri(url);
    string payload = JsonConvert.SerializeObject(request);
    HttpContent httpContent = new StringContent(payload, "application/json");

    HttpRequestMessage requestMessage = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        RequestUri = requestUri,
        Content = httpContent
    };

    return await SendUriAsync<T2>(requestMessage);
}

public static async Task<T> SendUriAsync<T>(HttpRequestMessage requestMessage)
{
   
    string clint_id = "1234";
    
      var clineCred = Encoding.UTF8.GetBytes($"{client_id}");
        
        using(var result = await client.SendAsync(requestMessage))
        {
            result.EnsureSuccessStatusCode();
            var response = await result.Content.ReadAsStringAsync();
            
            if(result.IsSuccessStatusCode && response != null)
            {
                return JsonConvert.DeserializeObject<T>(response);
            }
            else
            {
                return null;
            }
        }        
 
}

Here is the controller class which calls these methods:

CarSalesController.cs

string thirdpartyUrl = "someurl";
var responseObject = await HttpClientHelper.ExecuteRequest<CarObject, string>(requestObject, thirdpartyUrl);

I am getting error in the HttpClientHelper class. The error is:

ResponseStatusCode does not indicate success. StatusCode:401 - UnAuthorised

But the same API works well when I use postman. He re is the cURL

curl --location --request GET 'someurl'
--header 'client_id:1234'
--header 'Authorization: Basic asdf'
--header 'Content-Type: application/json'
--data-raw '{ "data1":"somedata1", "data2":"somedata2" }'

What could be wrong in my code?

Learner
  • 15
  • 8
  • Try : CreateRequest – jdweng Aug 25 '20 at 05:39
  • @jdweng This change did not work – Learner Aug 25 '20 at 05:46
  • 1
    Shouldn’t you serialize (instead of deserialize) the object in the last line of CreateRequest method? – JJuice Aug 25 '20 at 06:04
  • 1
    You are deserializing response twice – Fabio Aug 25 '20 at 06:05
  • @JJuice When I serialize the object in the last line of CreateRequest method, it says "cannot implicitly convert string to TOut" – Learner Aug 25 '20 at 07:38
  • `JsonConvert.DeserializeObject` in `CreateRequest` is a synchronous implementation right? You should not be `await`ing that. – trashr0x Aug 25 '20 at 08:04
  • Should consider your method namings. Method CreateRequest does not "create a request" as it promises. It should be something more like "ExecuteRequest". Also consider introducing interface for better testability. – Mika Karjunen Aug 25 '20 at 09:57
  • 1
    @MikaKarjunen thank you for the tips. I have modified the code accordingly – Learner Aug 25 '20 at 12:43
  • In your postman example you are using GET method yet in code POST is used. Could this be a part of the problem. Also you are not sending "client_id" -header as in your example. If you are not using SSL you can listen the traffic pretty easily to see what is being sent and received exactly. – Mika Karjunen Aug 26 '20 at 08:14
  • When I used HttpMethod.Get in the HttpRequestMessage, it says "Cannot send a content-body with this verb-type". I need to send carObject with this call. Also, I am not sure how to send the client_id header. Could you please show me a code. – Learner Aug 26 '20 at 08:57
  • Sure buddy. Hold my beer... – Mika Karjunen Aug 26 '20 at 12:30

2 Answers2

2

Isn't this what you are trying to do?

public async Task<TOut> ExecuteRequestAsync<TIn, TOut>( TIn request, string url )
{
    var requestUri = new Uri( url );
    var payload = JsonConvert.SerializeObject( request );
    var httpContent = new StringContent( payload, Encoding.UTF8, "application/json" );
    var requestMessage = new HttpRequestMessage()
    {
        Method = HttpMethod.Post,
        RequestUri = requestUri,
        Content = httpContent
    };
    return await SendUriAsync<TOut>( requestMessage );
}

public static async Task<T> SendUriAsync<T>( HttpRequestMessage requestMessage )
{
    using (var client = CreateClient( 1234 ))
    {
        var result = await client.SendAsync( requestMessage );
        result.EnsureSuccessStatusCode();
        var response = await result.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>( response );
    }
}

private static HttpClient CreateClient(int clientId)
{
    var clientCred = Encoding.UTF8.GetBytes( clientId.ToString() );
    var auth = Convert.ToBase64String( clientCred );
    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", auth );
    client.DefaultRequestHeaders.Add( "client_id", "1234" );
    return client;
}
Mika Karjunen
  • 342
  • 4
  • 9
  • 1
    Thank you. This worked like a charm. I was doing unnecessary conversions. However, when I executed the code, it gives error at the client.SendAsync(requestMessage) line saying "Unable to read data from the transport connection. An existing connection was forcibly closed by the remote host". What may be causing this issue. Is it something to do with the HttpClient. I am using .Net 4.7.2 – Learner Aug 25 '20 at 12:51
  • Glad to be of help. Ensure, that the url is correct and the service is found. Consider trying a test call with fiddler or any similar tool. Also you might want to add call "result.EnsureSuccessStatusCode();" right after client.SendAsync. – Mika Karjunen Aug 26 '20 at 07:14
  • I have added the above code. But I get a different error now. I have modified the code in the question. Could you please have a look – Learner Aug 26 '20 at 08:01
  • Here. I added credentials and additional header information. You should consider moving the creation of HttpClient into a FactoryClass that takes care of those things. – Mika Karjunen Aug 26 '20 at 13:00
  • Thank you. I have tried the above code and I get an error. ResponseStatus - Forbidden. Looks like the credentials are wrong. But I have provided the right client_Id – Learner Aug 26 '20 at 13:31
  • In the postman, it works only when it is a Get command. I am using Post in code to send the data with the request. Is there a way to send json data with a Get command – Learner Aug 26 '20 at 14:30
  • Dear Learner, by definition GET -verb can be only used for "getting" stuff. Not sending. When using GET everything that needs to be sent to the server must go through eighter in querystring or header-values. If you are, for instance, adding new record you need to use POST or PUT verb. Generally POST. – Mika Karjunen Aug 27 '20 at 06:20
  • Thank you.. I understand this. Still I do not get how the cUrl command works in postman. Anyways I am changing the spec to a GET with querystring – Learner Sep 03 '20 at 10:49
  • I hope you'll eventually can make the software to work with post -commands as well. The issue seems to be on the api service. – Mika Karjunen Sep 07 '20 at 15:32
1

The problem might be with the DeserializeObject. Try using:

response.Content.ReadAsStringAsync()
Enigmativity
  • 113,464
  • 11
  • 89
  • 172