0

Using C# - How may I have the function return the response back to the main program?

I tried adding Task string to the function along with adding string response = cls.PostData(accesstoken, thejsondata).Wait(); to the call from Main. I am guessing I am not understanding the asynchronous aspect.

// From the main program
CallingClass cls = new CallingClass();
cls.PostData(accesstoken, thejsondata).Wait();
// want the "body" of data returned here?

public class CallingClass
{

  private static readonly HttpClient client = new HttpClient();


        public async Task PostData(string accesstoken, string thejsondata)
        {
                      
            var request = new HttpRequestMessage
            {
                Method = HttpMethod.Post,
                RequestUri = new Uri("https://whateverurl.com/"),              
                
                Headers =
                {
                    { "Authorization", accesstoken }
                },
                */
                Content = new StringContent(thejsondata))

                {
                    Headers =
                    {
                        ContentType = new MediaTypeHeaderValue("application/json")
                    }
                }
            };
            using (var response = await client.SendAsync(request))
            {
                response.EnsureSuccessStatusCode();
                var body = await response.Content.ReadAsStringAsync();
                Int32 responseStatusCode = (Int32)response.StatusCode;
                if (responseStatusCode.ToString() == "200" || responseStatusCode.ToString() == "201")
                {
                    hlp.AddToOutputFolder("StatusCode -  " + responseStatusCode.ToString());
                    hlp.AddToOutputFolder("Response -  " + body);
                }
                else
                {
                    hlp.AddToOutputFolder("ERROR RESPONSE STRING= " + responseStatusCode.ToString());
                }

                //return body;

            }
           
        }

}
Rob
  • 1
  • 1
  • When you uncomment `return body;` in what way does it fail? – David Apr 17 '23 at 19:53
  • Does this answer your question? [How to make an Asynchronous Method return a value?](https://stackoverflow.com/questions/6045343/how-to-make-an-asynchronous-method-return-a-value) – Charlieface Apr 17 '23 at 20:52
  • 1
    Side note: `if (responseStatusCode.ToString() == "200" || responseStatusCode.ToString() == "201")` why not just the much simpler `if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created)` – Charlieface Apr 17 '23 at 20:54
  • Change the return type of the method to `Task` and then call `return body;` – Roberto Ferraris Apr 18 '23 at 16:51

1 Answers1

1

You are returning a Task which is going to be giving you back void. You won't be able to store that response to a variable for later use in Main(). What you need to do is change the return type, await the method, and assign to a var for use

public class App
{
    public static async Task Main(string[] args)
    {
        CallingClass cls = new CallingClass();
        string postResponse = await cls.PostData(accesstoken, thejsondata); //don't need .Wait(), use await

        //now do stuff with postResponse...
    }
}
public class CallingClass
{
    private static readonly HttpClient client = new HttpClient();


    public async Task<string> PostData(string accesstoken, string thejsondata)
    {
        HttpRequestMessage request = new()
        {
            Method = HttpMethod.Post,
            RequestUri = new Uri("https://whateverurl.com/"),              
            Headers =
            {
                { "Authorization", accesstoken }
            },
            Content = new StringContent(thejsondata)
            {
                Headers =
                {
                    ContentType = new MediaTypeHeaderValue("application/json")
                }
            }
        };

        using (response = await client.SendAsync(request))
        {
            response.EnsureSuccessStatusCode();
            string body = await response.Content.ReadAsStringAsync();
            int responseStatusCode = (int) response.StatusCode;
            if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created)
            {
                hlp.AddToOutputFolder("StatusCode -  " + responseStatusCode.ToString());
                hlp.AddToOutputFolder("Response -  " + body);
            }
            else
            {
                hlp.AddToOutputFolder("ERROR RESPONSE STRING= " + responseStatusCode.ToString());
            }

            return body;
        }
    }
}

You can never assign a void method's "return" to a variable, not even with the discard operator _ = MyVoidMethod(); so its the same sort of thing here. A void method returns nothing. await is going to unwrap what you actually need from Task<T> and that T must match the typing of the variable you want to assign the return to

You should also give this SO question a read as the await operator and the .Wait() method are different things altogether and cannot be used interchangeably

Narish
  • 607
  • 4
  • 18
  • If I try adding the await to the call in Main, I get - The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. – Rob Apr 17 '23 at 20:50
  • Ok so this is because there is a difference between your standard main method and an async main method. With .NET 5+ they decided to hide the main method by default (compiler will throw it in for you). I could've sworn that it is *supposed* to also handle the change from a sync vs async Main() on its own...let me look into this – Narish Apr 17 '23 at 20:57
  • You absolutely should give [this](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements) a read – Narish Apr 17 '23 at 20:59
  • I have added the fully written out async version of Main in an edit to my response. I am not sure how you are able to use top level statements and getting that error....I have never had the issue before myself – Narish Apr 17 '23 at 21:03
  • What if I call PostData in the following manner CallingClass cls = new CallingClass(); string response = cls.PostData(accesstoken, thejsondata).Result; Is this acceptable? – Rob Apr 17 '23 at 21:50
  • Let me step back a bit. I may be approaching this wrong. I am used to writing WCF services that perform synchronous tasks. To date I have never had any issues. The current requirement is for the WCF service to make several http POSTs back-to-back with dependencies. The http client runs asynchronously. First call - Get Token – need the results from this call in order to make second call Second call - Get the proper Id - need the results from this call in order to make third call Third Call - Post transaction and interrogate the results – Rob Apr 17 '23 at 23:15
  • `.Result` is a easy way to shoot yourself in the foot with async work, bc it will block on its thread and lead to deadlocks in the application. I learned this the hard way once with a ASP.NET application. When using HttpClient, you absolutely *should* follow the proper async patterns of making everything Tasks and awaiting them properly – Narish Apr 18 '23 at 13:58
  • What do you mean by synchronous tasks? HttpClient is the new standard and demands async, for good reason. You technically can use the old `HttpWebRequest` way using things like `.GetResponse()` but the documentation [explictly warns against doing so](https://learn.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest?view=net-8.0) – Narish Apr 18 '23 at 14:01
  • Did my code snippet work for you? If you are still getting an error please post the exact error message, I'm not sure why exactly this isn't working or not ideal – Narish Apr 18 '23 at 14:03